@cecwxf/dsh-room 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.js CHANGED
@@ -37,7 +37,7 @@ var import_react = require("react");
37
37
  var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
38
38
 
39
39
  // src/client/rooms.css
40
- var rooms_default = '/* Collab Rooms UI \u2014 original surface inside the dsh shell. Uses only dsh --dsw-* tokens.\n Independent Rooms surface. Prefix: rm- (rooms). */\n\n.rm-root {\n --rm-accent: var(--dsw-alias-state-business-primary);\n --rm-hover: var(--dsw-alias-interactive-bg-hover);\n --rm-border: var(--dsw-alias-border-l2);\n --rm-bg: var(--dsw-alias-bg-base);\n --rm-panel: var(--dsw-alias-bg-module-platform);\n --rm-label: var(--dsw-alias-label-primary);\n --rm-muted: var(--dsw-alias-label-secondary);\n --rm-quiet: var(--dsw-alias-label-tertiary);\n font: 13px/1.5 var(--dsw-font-family);\n color: var(--rm-label);\n box-sizing: border-box;\n}\n.rm-root *, .rm-root *::before, .rm-root *::after { box-sizing: border-box; }\n.rm-root button, .rm-root input, .rm-root textarea { font: inherit; }\n\n/* \u2500\u2500 sidebar (joined rooms + public search) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-side {\n display: flex; flex-direction: column; height: 100%; min-height: 0;\n padding: 0 8px 8px 0; gap: 8px;\n}\n.rm-side-toolbar {\n display: flex; align-items: center; gap: 6px; flex: none; padding: 4px 0 2px;\n}\n.rm-side-title {\n flex: 1; font-size: 12px; font-weight: 600; letter-spacing: .04em;\n text-transform: uppercase; color: var(--rm-quiet);\n}\n.rm-icon-btn {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; padding: 0;\n}\n.rm-icon-btn:hover { background: var(--rm-hover); color: var(--rm-label); }\n.rm-search {\n display: flex; align-items: center; gap: 6px; height: 32px; padding: 0 10px;\n border: .5px solid var(--rm-border); border-radius: 8px; background: var(--rm-panel);\n}\n.rm-search input {\n flex: 1; min-width: 0; border: 0; background: transparent; outline: none; color: var(--rm-label); font-size: 13px;\n}\n.rm-search input::placeholder { color: var(--rm-quiet); }\n.rm-side-scroll { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 12px; }\n.rm-group-label { font-size: 11px; font-weight: 600; color: var(--rm-quiet); padding: 0 4px 4px; text-transform: uppercase; letter-spacing: .03em; }\n.rm-room-list { display: flex; flex-direction: column; gap: 2px; }\n.rm-room-item {\n display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px;\n border: 0; border-radius: 10px; background: transparent; color: inherit; cursor: pointer; text-align: left;\n}\n.rm-room-item:hover { background: var(--rm-hover); }\n.rm-room-item[aria-current="true"] { background: var(--rm-hover); }\n.rm-room-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }\n.rm-room-item-name { font-weight: 600; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-meta { font-size: 11.5px; color: var(--rm-quiet); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-trail { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }\n.rm-badge {\n display: inline-flex; align-items: center; gap: 3px; height: 18px; padding: 0 6px;\n border-radius: 999px; font-size: 10.5px; font-weight: 500; background: var(--rm-panel); color: var(--rm-muted);\n border: .5px solid var(--rm-border);\n}\n.rm-badge-public { color: var(--rm-accent); border-color: color-mix(in srgb, var(--rm-accent) 35%, transparent); background: color-mix(in srgb, var(--rm-accent) 8%, transparent); }\n.rm-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--rm-quiet); }\n.rm-dot-online { background: var(--dsw-alias-state-success-primary, #2bb673); }\n.rm-dot-connecting { background: var(--dsw-alias-state-warn-primary, #e9a23b); }\n.rm-dot-offline { background: var(--dsw-alias-state-error-primary, #e5484d); }\n.rm-empty-hint { padding: 8px 10px; font-size: 12px; color: var(--rm-quiet); line-height: 1.45; }\n.rm-dir-item {\n display: flex; align-items: flex-start; gap: 10px; padding: 10px; border-radius: 10px;\n border: .5px solid var(--rm-border); background: var(--rm-bg); margin-bottom: 6px;\n}\n.rm-dir-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }\n.rm-dir-tags { display: flex; flex-wrap: wrap; gap: 4px; }\n.rm-tag { font-size: 11px; color: var(--rm-muted); background: var(--rm-hover); border-radius: 4px; padding: 0 5px; }\n.rm-menu {\n position: fixed; z-index: 1000; min-width: 180px; padding: 4px;\n border-radius: 10px; background: var(--dsw-alias-bg-layer-1, var(--rm-bg));\n border: .5px solid var(--rm-border);\n box-shadow: 0 8px 24px color-mix(in srgb, var(--rm-label) 12%, transparent);\n display: flex; flex-direction: column;\n}\n.rm-menu button {\n display: flex; align-items: center; gap: 8px; height: 32px; padding: 0 10px;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-menu button:hover { background: var(--rm-hover); }\n\n/* \u2500\u2500 main: room workspace \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-main { display: flex; flex: 1; min-height: 0; height: 100%; width: 100%; background: var(--rm-bg); }\n.rm-workspace { display: flex; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat-header {\n flex: none; display: flex; align-items: center; gap: 10px; height: 48px;\n padding: 0 16px; border-bottom: .5px solid var(--rm-border);\n}\n.rm-chat-header-title { font-size: 14px; font-weight: 600; }\n.rm-chat-header-sub { font-size: 12px; color: var(--rm-quiet); }\n.rm-messages {\n flex: 1; min-height: 0; overflow: auto; padding: 16px 18px 8px;\n display: flex; flex-direction: column; gap: 2px;\n}\n.rm-day {\n align-self: center; margin: 10px 0 12px; padding: 2px 10px; border-radius: 999px;\n font-size: 11px; color: var(--rm-quiet); background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n}\n.rm-msg {\n display: grid; grid-template-columns: 32px 1fr; gap: 0 10px; padding: 4px 6px; border-radius: 8px;\n max-width: 720px;\n}\n.rm-msg:hover { background: color-mix(in srgb, var(--rm-hover) 60%, transparent); }\n.rm-msg-self { margin-left: auto; grid-template-columns: 1fr; max-width: 640px; }\n.rm-msg-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 1px; }\n.rm-msg-author { font-size: 12px; font-weight: 600; color: var(--rm-muted); }\n.rm-msg-time { font-size: 11px; color: var(--rm-quiet); }\n.rm-msg-body { font-size: 13.5px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; }\n.rm-msg-self .rm-msg-body {\n background: var(--dsw-alias-label-primary); color: var(--dsw-alias-label-primary-foreground);\n padding: 8px 12px; border-radius: 12px 12px 4px 12px; width: fit-content; margin-left: auto;\n}\n.rm-msg-event {\n grid-template-columns: 1fr; max-width: 560px; margin: 6px 0;\n padding: 8px 12px; border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-panel);\n}\n.rm-mention { color: var(--rm-accent); font-weight: 500; }\n.rm-msg-self .rm-mention { color: inherit; text-decoration: underline; text-underline-offset: 2px; }\n.rm-task-card { display: flex; flex-direction: column; gap: 4px; }\n.rm-task-head { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--rm-muted); }\n.rm-task-title { font-weight: 600; font-size: 13.5px; }\n.rm-composer {\n flex: none; display: flex; align-items: flex-end; gap: 8px; padding: 10px 16px 14px;\n border-top: .5px solid var(--rm-border);\n}\n.rm-composer-box {\n flex: 1; display: flex; align-items: flex-end; gap: 6px; min-height: 44px;\n padding: 8px 10px; border: .5px solid var(--rm-border); border-radius: 12px; background: var(--rm-panel);\n}\n.rm-composer-box:focus-within { border-color: color-mix(in srgb, var(--rm-accent) 50%, var(--rm-border)); }\n.rm-composer-box textarea {\n flex: 1; min-height: 28px; max-height: 120px; border: 0; background: transparent; outline: none;\n resize: none; color: var(--rm-label); line-height: 1.45;\n}\n.rm-composer-box textarea::placeholder { color: var(--rm-quiet); }\n.rm-composer-mode {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; flex: none;\n}\n.rm-composer-mode:hover, .rm-composer-mode[aria-pressed="true"] {\n color: var(--rm-accent); background: color-mix(in srgb, var(--rm-accent) 12%, transparent);\n}\n.rm-chat-hint { margin: 48px auto; text-align: center; color: var(--rm-quiet); font-size: 13px; max-width: 360px; line-height: 1.5; }\n\n/* \u2500\u2500 detail column (members / invite / about) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-detail {\n flex: 0 0 280px; width: 280px; display: flex; flex-direction: column; min-height: 0;\n border-left: .5px solid var(--rm-border); background: var(--rm-bg); overflow: auto;\n}\n.rm-detail-section { padding: 14px 14px 8px; border-bottom: .5px solid var(--rm-border); }\n.rm-detail-section:last-child { border-bottom: 0; }\n.rm-detail-h {\n display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600;\n text-transform: uppercase; letter-spacing: .04em; color: var(--rm-quiet); margin-bottom: 8px;\n}\n.rm-member {\n display: flex; align-items: center; gap: 8px; padding: 5px 4px; border-radius: 8px; min-height: 34px;\n}\n.rm-member:hover { background: var(--rm-hover); }\n.rm-member-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }\n.rm-member-actions { display: none; gap: 2px; }\n.rm-member:hover .rm-member-actions, .rm-member:focus-within .rm-member-actions { display: inline-flex; }\n.rm-mini {\n width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-muted); cursor: pointer; font-size: 12px;\n}\n.rm-mini:hover { background: var(--dsw-alias-interactive-bg-active); color: var(--rm-label); }\n.rm-task-row { display: flex; align-items: center; gap: 6px; padding: 4px; font-size: 12.5px; }\n.rm-task-row-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-about { display: grid; grid-template-columns: 88px 1fr; gap: 6px 8px; font-size: 12.5px; margin: 0; }\n.rm-about dt { color: var(--rm-quiet); margin: 0; }\n.rm-about dd { margin: 0; min-width: 0; overflow-wrap: anywhere; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }\n.rm-mono { font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace); font-size: 11.5px; }\n.rm-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }\n.rm-danger { color: var(--dsw-alias-state-error-primary, #e5484d) !important; }\n.rm-note { font-size: 12px; color: var(--rm-quiet); line-height: 1.45; margin: 0 0 8px; }\n\n/* \u2500\u2500 empty / welcome \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-welcome-wrap {\n flex: 1; display: flex; align-items: center; justify-content: center; padding: 32px; background: var(--rm-bg);\n}\n.rm-welcome {\n width: min(420px, 100%); display: flex; flex-direction: column; gap: 10px;\n padding: 20px; border-radius: 14px; border: .5px solid var(--rm-border); background: var(--rm-panel);\n}\n.rm-welcome h1 { margin: 0; font-size: 16px; font-weight: 600; }\n.rm-welcome p { margin: 0; font-size: 13px; color: var(--rm-muted); line-height: 1.5; }\n.rm-welcome-actions { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }\n.rm-welcome-action {\n display: flex; align-items: flex-start; gap: 10px; width: 100%; padding: 10px 12px;\n border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-bg);\n color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-welcome-action:hover { background: var(--rm-hover); }\n.rm-welcome-action b { display: block; font-size: 13px; }\n.rm-welcome-action span { display: block; font-size: 12px; color: var(--rm-quiet); margin-top: 2px; }\n\n/* \u2500\u2500 avatars \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-avatar {\n display: inline-flex; align-items: center; justify-content: center; flex: none;\n font-weight: 600; letter-spacing: .02em; user-select: none;\n}\n.rm-avatar-wrap { position: relative; display: inline-flex; flex: none; }\n\n/* \u2500\u2500 dialogs (inside dsh Modal) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-form { display: flex; flex-direction: column; gap: 14px; }\n.rm-field { display: flex; flex-direction: column; gap: 6px; }\n.rm-field-label { font-size: 13px; font-weight: 500; }\n.rm-field-hint { font-size: 12px; color: var(--rm-quiet); }\n.rm-name-row { display: flex; align-items: center; gap: 10px; }\n.rm-name-row > :last-child { flex: 1; }\n.rm-switch-row {\n display: flex; align-items: center; justify-content: space-between; gap: 14px;\n padding: 10px 12px; border-radius: 10px; background: var(--rm-panel);\n}\n.rm-error { color: var(--dsw-alias-state-error-primary, #e5484d); font-size: 12.5px; }\n.rm-invite {\n display: block; padding: 10px 12px; border-radius: 8px; background: var(--rm-panel);\n font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace);\n font-size: 12px; word-break: break-all; user-select: all;\n}\n\n/* Dual transport + approvals + agent config */\n.rm-mode-row { display: flex; gap: 8px; }\n.rm-mode-btn {\n flex: 1; padding: 10px 12px; border-radius: 10px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer; font: inherit;\n}\n.rm-mode-btn.is-on { border-color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent); }\n.rm-lan-browse { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; align-items: center; }\n.rm-lan-peer {\n font-size: 12px; padding: 4px 8px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer;\n}\n.rm-approvals { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); background: color-mix(in srgb, #f5a524 12%, transparent); }\n.rm-approval { display: flex; gap: 12px; align-items: center; justify-content: space-between; }\n.rm-approval-body { font-size: 13px; }\n.rm-approval-actions { display: flex; gap: 6px; flex-shrink: 0; }\n.rm-input { width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; font: inherit; }\n\n.rm-ws-bar { display: flex; flex-direction: column; gap: 4px; padding: 8px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); }\n.rm-ws-chip { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; cursor: pointer; font: inherit; font-size: 12.5px; }\n.rm-ws-path { font-size: 11px; color: var(--rm-quiet, #8b93a7); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item.is-active { background: color-mix(in srgb, var(--rm-accent, #5b8cff) 12%, transparent); }\n\n/* Native-aligned room composer (mirrors dsh conversation control row) */\n.rm-composer-card {\n box-sizing: border-box;\n width: 100%;\n max-width: 920px;\n margin: 0 auto;\n border-radius: 16px;\n background: var(--rm-elev, var(--rm-panel, #12151c));\n border: 1px solid var(--rm-border, #2a2f3a);\n box-shadow: 0 1px 2px rgba(0,0,0,.06);\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 8px 0 6px;\n}\n.rm-composer-grow { position: relative; padding: 0 4px; }\n.rm-composer-grow textarea {\n box-sizing: border-box; width: 100%; min-height: 36px; max-height: 160px;\n resize: none; border: none; outline: none; background: transparent;\n color: inherit; font: inherit; font-size: 14px; line-height: 22px;\n padding: 4px 12px 0 14px;\n}\n.rm-composer-grow textarea:disabled { opacity: .55; cursor: not-allowed; }\n.rm-composer-row {\n display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;\n gap: 10px; min-width: 0; padding: 2px 8px 4px;\n}\n.rm-composer-tools, .rm-composer-modes, .rm-composer-trailing {\n display: flex; align-items: center; min-width: 0; gap: 8px;\n}\n.rm-composer-trailing { flex: none; margin-left: auto; }\n.rm-composer-add {\n width: 28px; height: 28px; border: none; border-radius: 999px;\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n color: inherit; cursor: pointer; display: grid; place-items: center;\n}\n.rm-composer-add:hover, .rm-composer-add[aria-pressed="true"] {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 22%, transparent);\n}\n.rm-composer-select {\n max-width: 220px; height: 28px; border: none; border-radius: 8px;\n background: transparent; color: var(--rm-quiet, #8b93a7);\n font: inherit; font-size: 13px; font-weight: 500; line-height: 20px;\n padding: 0 8px; cursor: pointer; white-space: nowrap;\n}\n.rm-composer-select:hover:not(:disabled) {\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n}\n.rm-composer-select:disabled { opacity: .5; cursor: default; }\n.rm-composer-model-text { display: inline; }\n.rm-composer-model-icon { display: none; font-size: 10px; }\n.rm-composer-effort {\n margin-left: 6px; font-size: 11px; opacity: .75;\n text-transform: uppercase; letter-spacing: .02em;\n}\n.rm-composer-send {\n width: 34px; height: 34px; border: none; border-radius: 999px;\n background: var(--rm-accent, #5b8cff); color: #fff; cursor: pointer;\n display: grid; place-items: center; transform: translateY(-2px);\n}\n.rm-composer-send:hover:not(:disabled) { filter: brightness(1.08); }\n.rm-composer-send:disabled { opacity: .4; cursor: default; }\n.rm-composer-popwrap { position: relative; }\n.rm-composer-menu {\n position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 40;\n min-width: 200px; max-height: 320px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 8px 24px rgba(0,0,0,.28);\n padding: 6px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-composer-menu-wide { min-width: 280px; max-width: 340px; }\n.rm-composer-menu-label {\n font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;\n color: var(--rm-quiet, #8b93a7); padding: 6px 8px 2px;\n}\n.rm-composer-menu button[role="menuitem"] {\n text-align: left; border: none; background: transparent; color: inherit;\n font: inherit; font-size: 13px; padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-composer-menu button[role="menuitem"]:hover,\n.rm-composer-menu button[role="menuitem"].is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent);\n}\n.rm-composer-check {\n display: flex; align-items: center; gap: 8px; font-size: 13px; padding: 4px 8px; cursor: pointer;\n}\n.rm-grant-row {\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n padding: 2px 8px; font-size: 12.5px;\n}\n.rm-grant-row select {\n font: inherit; font-size: 12px; border-radius: 6px;\n border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; padding: 2px 6px;\n}\n.rm-peer-grant-block { border-top: 1px solid var(--rm-border, #2a2f3a); padding-top: 4px; margin-top: 4px; }\n.rm-peer-grant-name { font-size: 12px; font-weight: 600; padding: 4px 8px; overflow: hidden; text-overflow: ellipsis; }\n\n/* Hide legacy tight composer if still referenced */\n.rm-composer { display: none; }\n\n.rm-composer-grow { position: relative; }\n.rm-mention-menu {\n position: absolute; left: 8px; right: 8px; bottom: calc(100% + 6px); z-index: 50;\n max-height: 240px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 10px 28px rgba(0,0,0,.32);\n padding: 4px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-mention-item {\n display: flex; align-items: center; gap: 8px; text-align: left;\n border: none; background: transparent; color: inherit; font: inherit;\n padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-mention-item:hover, .rm-mention-item.is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 18%, transparent);\n}\n.rm-mention-item-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }\n.rm-mention-item-name { font-size: 13px; font-weight: 600; }\n.rm-mention-item-meta { font-size: 11px; color: var(--rm-quiet, #8b93a7); }\n\n\n/* New Room shortcut under workspace (mirrors New Session affordance) */\n.rm-new-room {\n display: flex; align-items: center; justify-content: center; gap: 6px;\n width: 100%; height: 36px; margin-top: 8px;\n border: .5px solid var(--rm-border); border-radius: 10px;\n background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n color: var(--rm-label); cursor: pointer; font-weight: 500; font-size: 13.5px;\n}\n.rm-new-room:hover { background: var(--dsw-alias-button-floating-hover, var(--rm-hover)); }\n.rm-new-room svg { flex: none; }\n\n/* Wide / landscape create + browse modals (overrides default 380px Modal card) */\n.rm-modal-wide {\n width: min(720px, calc(100vw - 48px)) !important;\n max-height: min(640px, calc(100vh - 48px));\n}\n.rm-form-wide {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px 18px;\n align-items: start;\n}\n.rm-form-wide .rm-span-2 { grid-column: 1 / -1; }\n.rm-browse { display: flex; flex-direction: column; gap: 12px; min-height: 280px; max-height: 420px; }\n.rm-browse-search { flex: none; }\n.rm-browse-status { font-size: 11.5px; color: var(--rm-quiet); white-space: nowrap; }\n.rm-browse-list { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 6px; }\n@media (max-width: 640px) {\n .rm-form-wide { grid-template-columns: 1fr; }\n .rm-modal-wide { width: min(420px, calc(100vw - 32px)) !important; }\n}\n';
40
+ var rooms_default = '/* Collab Rooms UI \u2014 original surface inside the dsh shell. Uses only dsh --dsw-* tokens.\n Independent Rooms surface. Prefix: rm- (rooms). */\n\n.rm-root {\n --rm-accent: var(--dsw-alias-state-business-primary);\n --rm-hover: var(--dsw-alias-interactive-bg-hover);\n --rm-border: var(--dsw-alias-border-l2);\n --rm-bg: var(--dsw-alias-bg-base);\n --rm-panel: var(--dsw-alias-bg-module-platform);\n --rm-label: var(--dsw-alias-label-primary);\n --rm-muted: var(--dsw-alias-label-secondary);\n --rm-quiet: var(--dsw-alias-label-tertiary);\n font: 13px/1.5 var(--dsw-font-family);\n color: var(--rm-label);\n box-sizing: border-box;\n}\n.rm-root *, .rm-root *::before, .rm-root *::after { box-sizing: border-box; }\n.rm-root button, .rm-root input, .rm-root textarea { font: inherit; }\n\n/* \u2500\u2500 sidebar (joined rooms + public search) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-side {\n display: flex; flex-direction: column; height: 100%; min-height: 0;\n padding: 0 8px 8px 0; gap: 8px;\n}\n.rm-side-toolbar {\n display: flex; align-items: center; gap: 6px; flex: none; padding: 4px 0 2px;\n}\n.rm-side-title {\n flex: 1; font-size: 12px; font-weight: 600; letter-spacing: .04em;\n text-transform: uppercase; color: var(--rm-quiet);\n}\n.rm-icon-btn {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; padding: 0;\n}\n.rm-icon-btn:hover { background: var(--rm-hover); color: var(--rm-label); }\n.rm-search {\n display: flex; align-items: center; gap: 6px; height: 32px; padding: 0 10px;\n border: .5px solid var(--rm-border); border-radius: 8px; background: var(--rm-panel);\n}\n.rm-search input {\n flex: 1; min-width: 0; border: 0; background: transparent; outline: none; color: var(--rm-label); font-size: 13px;\n}\n.rm-search input::placeholder { color: var(--rm-quiet); }\n.rm-side-scroll { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 12px; }\n.rm-group-label { font-size: 11px; font-weight: 600; color: var(--rm-quiet); padding: 0 4px 4px; text-transform: uppercase; letter-spacing: .03em; }\n.rm-room-list { display: flex; flex-direction: column; gap: 2px; }\n.rm-room-item {\n display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px;\n border: 0; border-radius: 10px; background: transparent; color: inherit; cursor: pointer; text-align: left;\n}\n.rm-room-item:hover { background: var(--rm-hover); }\n.rm-room-item[aria-current="true"] { background: var(--rm-hover); }\n.rm-room-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }\n.rm-room-item-name { font-weight: 600; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-meta { font-size: 11.5px; color: var(--rm-quiet); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-trail { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }\n.rm-badge {\n display: inline-flex; align-items: center; gap: 3px; height: 18px; padding: 0 6px;\n border-radius: 999px; font-size: 10.5px; font-weight: 500; background: var(--rm-panel); color: var(--rm-muted);\n border: .5px solid var(--rm-border);\n}\n.rm-badge-public { color: var(--rm-accent); border-color: color-mix(in srgb, var(--rm-accent) 35%, transparent); background: color-mix(in srgb, var(--rm-accent) 8%, transparent); }\n.rm-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--rm-quiet); }\n.rm-dot-online { background: var(--dsw-alias-state-success-primary, #2bb673); }\n.rm-dot-connecting { background: var(--dsw-alias-state-warn-primary, #e9a23b); }\n.rm-dot-offline { background: var(--dsw-alias-state-error-primary, #e5484d); }\n.rm-empty-hint { padding: 8px 10px; font-size: 12px; color: var(--rm-quiet); line-height: 1.45; }\n.rm-dir-item {\n display: flex; align-items: flex-start; gap: 10px; padding: 10px; border-radius: 10px;\n border: .5px solid var(--rm-border); background: var(--rm-bg); margin-bottom: 6px;\n}\n.rm-dir-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }\n.rm-dir-tags { display: flex; flex-wrap: wrap; gap: 4px; }\n.rm-tag { font-size: 11px; color: var(--rm-muted); background: var(--rm-hover); border-radius: 4px; padding: 0 5px; }\n.rm-menu {\n position: fixed; z-index: 1000; min-width: 180px; padding: 4px;\n border-radius: 10px; background: var(--dsw-alias-bg-layer-1, var(--rm-bg));\n border: .5px solid var(--rm-border);\n box-shadow: 0 8px 24px color-mix(in srgb, var(--rm-label) 12%, transparent);\n display: flex; flex-direction: column;\n}\n.rm-menu button {\n display: flex; align-items: center; gap: 8px; height: 32px; padding: 0 10px;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-menu button:hover { background: var(--rm-hover); }\n\n/* \u2500\u2500 main: room workspace \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-main { display: flex; flex: 1; min-height: 0; height: 100%; width: 100%; background: var(--rm-bg); }\n.rm-workspace { display: flex; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat-header {\n flex: none; display: flex; align-items: center; gap: 10px; height: 48px;\n padding: 0 16px; border-bottom: .5px solid var(--rm-border);\n}\n.rm-chat-header-title { font-size: 14px; font-weight: 600; }\n.rm-chat-header-sub { font-size: 12px; color: var(--rm-quiet); }\n.rm-messages {\n flex: 1; min-height: 0; overflow: auto; padding: 16px 18px 8px;\n display: flex; flex-direction: column; gap: 2px;\n}\n.rm-day {\n align-self: center; margin: 10px 0 12px; padding: 2px 10px; border-radius: 999px;\n font-size: 11px; color: var(--rm-quiet); background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n}\n.rm-msg {\n display: grid; grid-template-columns: 32px 1fr; gap: 0 10px; padding: 4px 6px; border-radius: 8px;\n max-width: 720px;\n}\n.rm-msg:hover { background: color-mix(in srgb, var(--rm-hover) 60%, transparent); }\n.rm-msg-self { margin-left: auto; grid-template-columns: 1fr; max-width: 640px; }\n.rm-msg-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 1px; }\n.rm-msg-author { font-size: 12px; font-weight: 600; color: var(--rm-muted); }\n.rm-msg-time { font-size: 11px; color: var(--rm-quiet); }\n.rm-msg-body { font-size: 13.5px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; }\n.rm-msg-self .rm-msg-body {\n background: var(--dsw-alias-label-primary); color: var(--dsw-alias-label-primary-foreground);\n padding: 8px 12px; border-radius: 12px 12px 4px 12px; width: fit-content; margin-left: auto;\n}\n.rm-msg-event {\n grid-template-columns: 1fr; max-width: 560px; margin: 6px 0;\n padding: 8px 12px; border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-panel);\n}\n.rm-mention { color: var(--rm-accent); font-weight: 500; }\n.rm-msg-self .rm-mention { color: inherit; text-decoration: underline; text-underline-offset: 2px; }\n.rm-task-card { display: flex; flex-direction: column; gap: 4px; }\n.rm-task-head { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--rm-muted); }\n.rm-task-title { font-weight: 600; font-size: 13.5px; }\n.rm-composer {\n flex: none; display: flex; align-items: flex-end; gap: 8px; padding: 10px 16px 14px;\n border-top: .5px solid var(--rm-border);\n}\n.rm-composer-box {\n flex: 1; display: flex; align-items: flex-end; gap: 6px; min-height: 44px;\n padding: 8px 10px; border: .5px solid var(--rm-border); border-radius: 12px; background: var(--rm-panel);\n}\n.rm-composer-box:focus-within { border-color: color-mix(in srgb, var(--rm-accent) 50%, var(--rm-border)); }\n.rm-composer-box textarea {\n flex: 1; min-height: 28px; max-height: 120px; border: 0; background: transparent; outline: none;\n resize: none; color: var(--rm-label); line-height: 1.45;\n}\n.rm-composer-box textarea::placeholder { color: var(--rm-quiet); }\n.rm-composer-mode {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; flex: none;\n}\n.rm-composer-mode:hover, .rm-composer-mode[aria-pressed="true"] {\n color: var(--rm-accent); background: color-mix(in srgb, var(--rm-accent) 12%, transparent);\n}\n.rm-chat-hint { margin: 48px auto; text-align: center; color: var(--rm-quiet); font-size: 13px; max-width: 360px; line-height: 1.5; }\n\n/* \u2500\u2500 detail column (members / invite / about) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-detail {\n flex: 0 0 280px; width: 280px; display: flex; flex-direction: column; min-height: 0;\n border-left: .5px solid var(--rm-border); background: var(--rm-bg); overflow: auto;\n}\n.rm-detail-section { padding: 14px 14px 8px; border-bottom: .5px solid var(--rm-border); }\n.rm-detail-section:last-child { border-bottom: 0; }\n.rm-detail-h {\n display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600;\n text-transform: uppercase; letter-spacing: .04em; color: var(--rm-quiet); margin-bottom: 8px;\n}\n.rm-member {\n display: flex; align-items: center; gap: 8px; padding: 5px 4px; border-radius: 8px; min-height: 34px;\n}\n.rm-member:hover { background: var(--rm-hover); }\n.rm-member-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }\n.rm-member-actions { display: none; gap: 2px; }\n.rm-member:hover .rm-member-actions, .rm-member:focus-within .rm-member-actions { display: inline-flex; }\n.rm-mini {\n width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-muted); cursor: pointer; font-size: 12px;\n}\n.rm-mini:hover { background: var(--dsw-alias-interactive-bg-active); color: var(--rm-label); }\n.rm-task-row { display: flex; align-items: center; gap: 6px; padding: 4px; font-size: 12.5px; }\n.rm-task-row-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-about { display: grid; grid-template-columns: 88px 1fr; gap: 6px 8px; font-size: 12.5px; margin: 0; }\n.rm-about dt { color: var(--rm-quiet); margin: 0; }\n.rm-about dd { margin: 0; min-width: 0; overflow-wrap: anywhere; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }\n.rm-mono { font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace); font-size: 11.5px; }\n.rm-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }\n.rm-danger { color: var(--dsw-alias-state-error-primary, #e5484d) !important; }\n.rm-note { font-size: 12px; color: var(--rm-quiet); line-height: 1.45; margin: 0 0 8px; }\n\n/* \u2500\u2500 empty / welcome \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-welcome-wrap {\n flex: 1; display: flex; align-items: center; justify-content: center; padding: 32px; background: var(--rm-bg);\n}\n.rm-welcome {\n width: min(420px, 100%); display: flex; flex-direction: column; gap: 10px;\n padding: 20px; border-radius: 14px; border: .5px solid var(--rm-border); background: var(--rm-panel);\n}\n.rm-welcome h1 { margin: 0; font-size: 16px; font-weight: 600; }\n.rm-welcome p { margin: 0; font-size: 13px; color: var(--rm-muted); line-height: 1.5; }\n.rm-welcome-actions { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }\n.rm-welcome-action {\n display: flex; align-items: flex-start; gap: 10px; width: 100%; padding: 10px 12px;\n border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-bg);\n color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-welcome-action:hover { background: var(--rm-hover); }\n.rm-welcome-action b { display: block; font-size: 13px; }\n.rm-welcome-action span { display: block; font-size: 12px; color: var(--rm-quiet); margin-top: 2px; }\n\n/* \u2500\u2500 avatars \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-avatar {\n display: inline-flex; align-items: center; justify-content: center; flex: none;\n font-weight: 600; letter-spacing: .02em; user-select: none;\n}\n.rm-avatar-wrap { position: relative; display: inline-flex; flex: none; }\n\n/* \u2500\u2500 dialogs (inside dsh Modal) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-form { display: flex; flex-direction: column; gap: 14px; }\n.rm-field { display: flex; flex-direction: column; gap: 6px; }\n.rm-field-label { font-size: 13px; font-weight: 500; }\n.rm-field-hint { font-size: 12px; color: var(--rm-quiet); }\n.rm-name-row { display: flex; align-items: center; gap: 10px; }\n.rm-name-row > :last-child { flex: 1; }\n.rm-switch-row {\n display: flex; align-items: center; justify-content: space-between; gap: 14px;\n padding: 10px 12px; border-radius: 10px; background: var(--rm-panel);\n}\n.rm-error { color: var(--dsw-alias-state-error-primary, #e5484d); font-size: 12.5px; }\n.rm-invite {\n display: block; padding: 10px 12px; border-radius: 8px; background: var(--rm-panel);\n font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace);\n font-size: 12px; word-break: break-all; user-select: all;\n}\n\n/* Dual transport + approvals + agent config */\n.rm-mode-row { display: flex; gap: 8px; }\n.rm-mode-btn {\n flex: 1; padding: 10px 12px; border-radius: 10px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer; font: inherit;\n}\n.rm-mode-btn.is-on { border-color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent); }\n.rm-lan-browse { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; align-items: center; }\n.rm-lan-peer {\n font-size: 12px; padding: 4px 8px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer;\n}\n.rm-approvals { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); background: color-mix(in srgb, #f5a524 12%, transparent); }\n.rm-approval { display: flex; gap: 12px; align-items: center; justify-content: space-between; }\n.rm-approval-body { font-size: 13px; }\n.rm-approval-actions { display: flex; gap: 6px; flex-shrink: 0; }\n.rm-input { width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; font: inherit; }\n\n.rm-ws-bar { display: flex; flex-direction: column; gap: 4px; padding: 8px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); }\n.rm-ws-chip { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; cursor: pointer; font: inherit; font-size: 12.5px; }\n.rm-ws-path { font-size: 11px; color: var(--rm-quiet, #8b93a7); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item.is-active { background: color-mix(in srgb, var(--rm-accent, #5b8cff) 12%, transparent); }\n\n/* Native-aligned room composer (mirrors dsh conversation control row) */\n.rm-composer-card {\n box-sizing: border-box;\n width: 100%;\n max-width: 920px;\n margin: 0 auto;\n border-radius: 16px;\n background: var(--rm-elev, var(--rm-panel, #12151c));\n border: 1px solid var(--rm-border, #2a2f3a);\n box-shadow: 0 1px 2px rgba(0,0,0,.06);\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 8px 0 6px;\n}\n.rm-composer-grow { position: relative; padding: 0 4px; }\n.rm-composer-grow textarea {\n box-sizing: border-box; width: 100%; min-height: 36px; max-height: 160px;\n resize: none; border: none; outline: none; background: transparent;\n color: inherit; font: inherit; font-size: 14px; line-height: 22px;\n padding: 4px 12px 0 14px;\n}\n.rm-composer-grow textarea:disabled { opacity: .55; cursor: not-allowed; }\n.rm-composer-row {\n display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;\n gap: 10px; min-width: 0; padding: 2px 8px 4px;\n}\n.rm-composer-tools, .rm-composer-modes, .rm-composer-trailing {\n display: flex; align-items: center; min-width: 0; gap: 8px;\n}\n.rm-composer-trailing { flex: none; margin-left: auto; }\n.rm-composer-add {\n width: 28px; height: 28px; border: none; border-radius: 999px;\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n color: inherit; cursor: pointer; display: grid; place-items: center;\n}\n.rm-composer-add:hover, .rm-composer-add[aria-pressed="true"] {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 22%, transparent);\n}\n.rm-composer-select {\n max-width: 220px; height: 28px; border: none; border-radius: 8px;\n background: transparent; color: var(--rm-quiet, #8b93a7);\n font: inherit; font-size: 13px; font-weight: 500; line-height: 20px;\n padding: 0 8px; cursor: pointer; white-space: nowrap;\n}\n.rm-composer-select:hover:not(:disabled) {\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n}\n.rm-composer-select:disabled { opacity: .5; cursor: default; }\n.rm-composer-model-text { display: inline; }\n.rm-composer-model-icon { display: none; font-size: 10px; }\n.rm-composer-effort {\n margin-left: 6px; font-size: 11px; opacity: .75;\n text-transform: uppercase; letter-spacing: .02em;\n}\n.rm-composer-send {\n width: 34px; height: 34px; border: none; border-radius: 999px;\n background: var(--rm-accent, #5b8cff); color: #fff; cursor: pointer;\n display: grid; place-items: center; transform: translateY(-2px);\n}\n.rm-composer-send:hover:not(:disabled) { filter: brightness(1.08); }\n.rm-composer-send:disabled { opacity: .4; cursor: default; }\n.rm-composer-popwrap { position: relative; }\n.rm-composer-menu {\n position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 40;\n min-width: 200px; max-height: 320px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 8px 24px rgba(0,0,0,.28);\n padding: 6px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-composer-menu-wide { min-width: 280px; max-width: 340px; }\n.rm-composer-menu-label {\n font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;\n color: var(--rm-quiet, #8b93a7); padding: 6px 8px 2px;\n}\n.rm-composer-menu button[role="menuitem"] {\n text-align: left; border: none; background: transparent; color: inherit;\n font: inherit; font-size: 13px; padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-composer-menu button[role="menuitem"]:hover,\n.rm-composer-menu button[role="menuitem"].is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent);\n}\n.rm-composer-check {\n display: flex; align-items: center; gap: 8px; font-size: 13px; padding: 4px 8px; cursor: pointer;\n}\n.rm-grant-row {\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n padding: 2px 8px; font-size: 12.5px;\n}\n.rm-grant-row select {\n font: inherit; font-size: 12px; border-radius: 6px;\n border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; padding: 2px 6px;\n}\n.rm-peer-grant-block { border-top: 1px solid var(--rm-border, #2a2f3a); padding-top: 4px; margin-top: 4px; }\n.rm-peer-grant-name { font-size: 12px; font-weight: 600; padding: 4px 8px; overflow: hidden; text-overflow: ellipsis; }\n\n/* Hide legacy tight composer if still referenced */\n.rm-composer { display: none; }\n\n.rm-composer-grow { position: relative; }\n.rm-mention-menu {\n position: absolute; left: 8px; right: 8px; bottom: calc(100% + 6px); z-index: 50;\n max-height: 240px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 10px 28px rgba(0,0,0,.32);\n padding: 4px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-mention-item {\n display: flex; align-items: center; gap: 8px; text-align: left;\n border: none; background: transparent; color: inherit; font: inherit;\n padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-mention-item:hover, .rm-mention-item.is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 18%, transparent);\n}\n.rm-mention-item-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }\n.rm-mention-item-name { font-size: 13px; font-weight: 600; }\n.rm-mention-item-meta { font-size: 11px; color: var(--rm-quiet, #8b93a7); }\n\n\n/* New Room shortcut under workspace (mirrors New Session affordance) */\n.rm-new-room {\n display: flex; align-items: center; justify-content: center; gap: 6px;\n width: 100%; height: 36px; margin-top: 8px;\n border: .5px solid var(--rm-border); border-radius: 10px;\n background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n color: var(--rm-label); cursor: pointer; font-weight: 500; font-size: 13.5px;\n}\n.rm-new-room:hover { background: var(--dsw-alias-button-floating-hover, var(--rm-hover)); }\n.rm-new-room svg { flex: none; }\n\n/* Wide / landscape create + browse modals (overrides default 380px Modal card) */\n.rm-modal-wide {\n width: min(720px, calc(100vw - 48px)) !important;\n max-height: min(640px, calc(100vh - 48px));\n}\n.rm-form-wide {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px 18px;\n align-items: start;\n}\n.rm-form-wide .rm-span-2 { grid-column: 1 / -1; }\n.rm-browse { display: flex; flex-direction: column; gap: 12px; min-height: 280px; max-height: 420px; }\n.rm-browse-search { flex: none; }\n.rm-browse-status { font-size: 11.5px; color: var(--rm-quiet); white-space: nowrap; }\n.rm-browse-list { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 6px; }\n@media (max-width: 640px) {\n .rm-form-wide { grid-template-columns: 1fr; }\n .rm-modal-wide { width: min(420px, calc(100vw - 32px)) !important; }\n}\n\n\n/* \u2500\u2500 panel layout: rooms nav (grouped by native dsh workspace) + content \u2500\u2500 */\n.rm-main { flex-direction: row; }\n.rm-main-body { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; }\n.rm-main-body > .rm-welcome-wrap { flex: 1; }\n.rm-main-body > .rm-workspace { flex: 1; }\n.rm-nav {\n flex: 0 0 248px; width: 248px; display: flex; flex-direction: column; gap: 8px; min-height: 0;\n padding: 10px 8px; border-right: .5px solid var(--rm-border); background: var(--rm-bg);\n}\n.rm-nav-head { display: flex; align-items: center; gap: 4px; padding: 0 4px; }\n.rm-nav-scroll { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 10px; }\n.rm-ws-group { display: flex; flex-direction: column; gap: 2px; }\n.rm-ws-head {\n display: flex; align-items: center; gap: 6px; height: 30px; padding: 0 2px 0 8px;\n font-size: 12.5px; font-weight: 600; color: var(--rm-muted);\n}\n.rm-ws-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-ws-add { width: 24px; height: 24px; }\n.rm-ws-empty { padding: 2px 10px 6px 30px; font-size: 11.5px; }\n.rm-nav .rm-room-item { padding: 6px 8px 6px 14px; }\n.rm-session-mark { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; color: var(--rm-accent, var(--dsw-alias-state-business-primary)); }\n';
41
41
 
42
42
  // src/client/store.js
43
43
  var API = "/api/collab";
@@ -153,13 +153,21 @@ function createRoomsStore() {
153
153
  searchTimer = setTimeout(() => void runSearch(query), 300);
154
154
  },
155
155
  async create(form) {
156
- const r = await post("/rooms", { ...form, workspaceId: form.workspaceId || snap.activeWorkspaceId });
156
+ const r = await post("/rooms", { ...form, workspaceId: form.workspacePath ? void 0 : form.workspaceId || snap.activeWorkspaceId });
157
+ try {
158
+ api.onRoomOpened && api.onRoomOpened();
159
+ } catch {
160
+ }
157
161
  set({ selected: r.roomId, dialog: { kind: "invite", roomId: r.roomId, name: r.name, invite: r.invite, public: r.public, listed: r.listed } });
158
162
  void loadMessages(r.roomId);
159
163
  return r;
160
164
  },
161
165
  async join(invite, info) {
162
- const r = await post("/join", { invite, workspaceId: snap.activeWorkspaceId, ...info || {} });
166
+ const r = await post("/join", { invite, ...info && info.workspacePath ? {} : { workspaceId: snap.activeWorkspaceId }, ...info || {} });
167
+ try {
168
+ api.onRoomOpened && api.onRoomOpened();
169
+ } catch {
170
+ }
163
171
  set((s) => ({ selected: r.roomId, dialog: null, search: { ...s.search, results: s.search.results.map((x) => x.roomId === r.roomId ? { ...x, joined: true } : x) } }));
164
172
  void loadMessages(r.roomId);
165
173
  toast(`Joined ${r.name}`);
@@ -209,7 +217,7 @@ function createRoomsStore() {
209
217
  return post(`/rooms/${encodeURIComponent(roomId)}/mcp`, { tool, args, target });
210
218
  }
211
219
  };
212
- return {
220
+ const api = {
213
221
  getSnapshot: () => snap,
214
222
  subscribe(fn) {
215
223
  subs.add(fn);
@@ -222,14 +230,20 @@ function createRoomsStore() {
222
230
  es = null;
223
231
  subs.clear();
224
232
  clearTimeout(searchTimer);
225
- }
233
+ },
234
+ /** Native dsh workspaces service (set by apply). */
235
+ native: null,
236
+ nativeList: null,
237
+ /** Called after a room is created/joined (apply switches to the DSH Room panel). */
238
+ onRoomOpened: null
226
239
  };
240
+ return api;
227
241
  }
228
242
 
229
243
  // src/client/index.jsx
230
244
  var import_jsx_runtime = require("react/jsx-runtime");
231
245
  var name = "dsh-room-client";
232
- var inject = ["slots", "layout"];
246
+ var inject = ["slots", "layout", "workspaces"];
233
247
  var PANEL_ID = "collab";
234
248
  var Svg = ({ size = 16, children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: size, height: size, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.15", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children });
235
249
  var RoomsIcon = ({ size }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size, children: [
@@ -242,7 +256,6 @@ var SearchIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { child
242
256
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m10.2 10.2 3.3 3.3" })
243
257
  ] });
244
258
  var PlusIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Svg, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 3v10M3 8h10" }) });
245
- var CloseIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Svg, { size: 14, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3.5 3.5l9 9M12.5 3.5l-9 9" }) });
246
259
  var SendIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Svg, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 13V3.5M4 7.5l4-4 4 4" }) });
247
260
  var LinkIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Svg, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6.5 9.5l3-3M7 4.75l1-1a2.5 2.5 0 0 1 3.5 3.5l-1 1M9 11.25l-1 1a2.5 2.5 0 0 1-3.5-3.5l1-1" }) });
248
261
  var TaskIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { children: [
@@ -287,212 +300,121 @@ var STATUS_TONE = { open: "info", claimed: "warning", done: "success", failed: "
287
300
  function useStore(store) {
288
301
  return (0, import_react.useSyncExternalStore)(store.subscribe, store.getSnapshot);
289
302
  }
290
- function renderMentions(text) {
291
- return text.split(/(@[A-Za-z0-9][\w.:-]*)/g).map((part, i) => part.startsWith("@") ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mention", children: part }, i) : part);
292
- }
293
- function PanelIcon({ size }) {
294
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsIcon, { size });
303
+ var EMPTY_WS = { items: [] };
304
+ var NO_SUB = () => () => {
305
+ };
306
+ var NO_SNAP = () => EMPTY_WS;
307
+ function useNativeWorkspaces(store) {
308
+ const src = store.nativeList;
309
+ const snap = (0, import_react.useSyncExternalStore)(src ? src.subscribe : NO_SUB, src ? src.getSnapshot : NO_SNAP);
310
+ return snap && snap.items || [];
311
+ }
312
+ var samePath = (a, b) => !!a && !!b && a.replace(/[\\/]+$/, "") === b.replace(/[\\/]+$/, "");
313
+ function groupRooms(rooms, workspaces) {
314
+ const groups = workspaces.map((w) => ({ ws: w, rooms: [] }));
315
+ const other = [];
316
+ for (const r of rooms) {
317
+ const path = r.memberConfig && r.memberConfig.workspacePath;
318
+ const g = groups.find((x) => samePath(x.ws.path, path));
319
+ if (g) g.rooms.push(r);
320
+ else other.push(r);
321
+ }
322
+ return { groups, other };
323
+ }
324
+ var wsTitle = (w) => w.title || (w.path || "").split(/[\\/]/).filter(Boolean).pop() || w.path;
325
+ function RoomItem({ r, selected, onClick }) {
326
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-room-item", "aria-current": selected ? "true" : void 0, "data-room-id": r.roomId, onClick, children: [
327
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-avatar-wrap", children: [
328
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: r.name, size: 26 }),
329
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-dot rm-dot-" + r.status, style: { position: "absolute", right: -1, bottom: -1, border: "2px solid var(--rm-bg)" } })
330
+ ] }),
331
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-room-item-body", children: [
332
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-name", children: r.name }),
333
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-meta", children: r.last ? `${r.last.from}: ${r.last.text}` : r.description || (r.public ? "Public room" : "Private \xB7 invite only") })
334
+ ] }),
335
+ r.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge rm-badge-public", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}) })
336
+ ] });
295
337
  }
296
- function RoomsSidebar({ store, ensurePanel }) {
338
+ function RoomsNav({ store }) {
297
339
  const s = useStore(store);
298
340
  const a = store.actions;
299
- const [menu, setMenu] = (0, import_react.useState)(null);
300
- const [joining, setJoining] = (0, import_react.useState)(null);
301
- const inputRef = (0, import_react.useRef)(null);
302
- const searching = !!s.search.open;
303
- const activeWs = (s.workspaces || []).find((w) => w.id === s.activeWorkspaceId) || (s.workspaces || [])[0] || null;
304
- (0, import_react.useEffect)(() => {
305
- if (!menu) return;
306
- const close = () => setMenu(null);
307
- window.addEventListener("click", close);
308
- return () => window.removeEventListener("click", close);
309
- }, [menu]);
310
- (0, import_react.useEffect)(() => {
311
- if (searching && inputRef.current) inputRef.current.focus();
312
- }, [searching]);
313
- const q = (s.search.query || "").trim().toLowerCase();
314
- const mine = (s.rooms || []).filter((r) => {
315
- if (activeWs && r.workspaceId && r.workspaceId !== activeWs.id) return false;
316
- if (!q) return true;
317
- return r.name.toLowerCase().includes(q) || (r.description || "").toLowerCase().includes(q) || (r.tags || []).some((t) => ("#" + t).includes(q));
318
- });
319
- const join = async (r) => {
320
- setJoining(r.roomId);
321
- try {
322
- await a.join(r.invite, { name: r.name, description: r.description, tags: r.tags, public: true, workspaceId: activeWs?.id });
323
- ensurePanel();
324
- } catch (e) {
325
- a.toast(e.message);
326
- } finally {
327
- setJoining(null);
328
- }
329
- };
330
- if (!activeWs) {
331
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-side", "data-collab-sidebar": "", children: [
332
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-side-toolbar", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-side-title", children: "Workspaces" }) }),
333
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-side-scroll", children: [
334
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-empty-hint", children: "Bind a local workspace first. Rooms you create inherit its path (MCP sandbox + agent cwd)." }),
335
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "primary", onClick: () => a.openDialog({ kind: "workspace" }), children: "Add workspace" })
336
- ] })
337
- ] });
338
- }
339
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-side", "data-collab-sidebar": "", children: [
340
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-side-toolbar", children: [
341
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-side-title", children: "DSH Room" }),
342
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-icon-btn", "aria-label": searching ? "Close search" : "Search rooms", "aria-pressed": searching, onClick: () => a.setSearchOpen(!searching), children: searching ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CloseIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}) }),
343
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
344
- "button",
345
- {
346
- type: "button",
347
- className: "rm-icon-btn",
348
- "data-rm-new": "",
349
- "aria-label": "New",
350
- "aria-haspopup": "menu",
351
- "aria-expanded": !!menu,
352
- onClick: (e) => {
353
- e.stopPropagation();
354
- const r = e.currentTarget.getBoundingClientRect();
355
- setMenu(menu ? null : { top: r.bottom + 4, left: Math.max(8, r.right - 200) });
356
- },
357
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {})
358
- }
359
- ),
360
- menu ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-menu", role: "menu", style: { top: menu.top, left: menu.left }, onClick: (e) => e.stopPropagation(), children: [
361
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
362
- setMenu(null);
363
- a.openDialog({ kind: "create" });
364
- }, children: [
365
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}),
366
- "Create room in workspace"
367
- ] }),
368
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
369
- setMenu(null);
370
- a.openDialog({ kind: "join" });
371
- }, children: [
372
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkIcon, {}),
373
- "Join by invite"
374
- ] }),
375
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
376
- setMenu(null);
377
- a.openDialog({ kind: "workspace" });
378
- }, children: [
379
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
380
- "Add / switch workspace"
381
- ] }),
382
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
383
- setMenu(null);
384
- a.openDialog({ kind: "browse" });
385
- }, children: [
386
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
387
- "Browse public rooms"
388
- ] })
389
- ] }) : null
341
+ const workspaces = useNativeWorkspaces(store);
342
+ const [q, setQ] = (0, import_react.useState)("");
343
+ const qq = q.trim().toLowerCase();
344
+ const match = (r) => !qq || r.name.toLowerCase().includes(qq) || (r.description || "").toLowerCase().includes(qq) || (r.tags || []).some((t) => ("#" + t).includes(qq));
345
+ const { groups, other } = groupRooms((s.rooms || []).filter(match), workspaces);
346
+ const sel = (r) => a.select(r.roomId);
347
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("nav", { className: "rm-nav", "aria-label": "Rooms by workspace", "data-rm-nav": "", children: [
348
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-nav-head", children: [
349
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-side-title", children: "Rooms" }),
350
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-icon-btn", title: "Browse public rooms", "aria-label": "Browse public rooms", onClick: () => a.openDialog({ kind: "browse" }), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}) }),
351
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-icon-btn", title: "Join by invite", "aria-label": "Join by invite", onClick: () => a.openDialog({ kind: "join" }), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkIcon, {}) })
390
352
  ] }),
391
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-bar", children: [
392
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-ws-chip", onClick: () => a.openDialog({ kind: "workspace" }), title: activeWs.path, children: [
393
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
394
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: activeWs.name })
395
- ] }),
396
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-path", title: activeWs.path, children: activeWs.path }),
397
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-new-room", "data-rm-new-room": "", onClick: () => {
398
- a.openDialog({ kind: "create" });
399
- ensurePanel();
400
- }, children: [
401
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}),
402
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "New Room" })
403
- ] })
404
- ] }),
405
- searching ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search", children: [
353
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search", children: [
406
354
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
407
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { ref: inputRef, type: "text", placeholder: "Search rooms or #tags", value: s.search.query, onChange: (e) => a.setQuery(e.target.value), onKeyDown: (e) => e.key === "Escape" && a.setSearchOpen(false), "aria-label": "Search rooms" })
408
- ] }) : null,
409
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-side-scroll", children: [
410
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
411
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-group-label", children: [
412
- "Rooms in ",
413
- activeWs.name
414
- ] }),
415
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-room-list", children: [
416
- mine.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
355
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "text", placeholder: "Filter rooms or #tags", value: q, onChange: (e) => setQ(e.target.value), "aria-label": "Filter rooms" })
356
+ ] }),
357
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-nav-scroll", children: [
358
+ groups.map(({ ws, rooms }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-group", "data-rm-ws-group": ws.workspaceId, children: [
359
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-head", title: ws.path, children: [
360
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
361
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-name", children: wsTitle(ws) }),
362
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
417
363
  "button",
418
364
  {
419
365
  type: "button",
420
- className: "rm-room-item",
421
- "aria-current": r.roomId === s.selected ? "true" : void 0,
422
- "data-room-id": r.roomId,
423
- onClick: () => {
424
- a.select(r.roomId);
425
- ensurePanel();
426
- },
427
- children: [
428
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-avatar-wrap", children: [
429
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: r.name, size: 30 }),
430
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-dot rm-dot-" + r.status, style: { position: "absolute", right: -1, bottom: -1, border: "2px solid var(--rm-bg)" } })
431
- ] }),
432
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-room-item-body", children: [
433
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-name", children: r.name }),
434
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-meta", children: r.last ? `${r.last.from}: ${r.last.text}` : r.description || (r.public ? "Public room" : "Private \xB7 invite only") })
435
- ] }),
436
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-room-item-trail", children: [
437
- r.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge rm-badge-public", children: [
438
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
439
- "Public"
440
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge", children: [
441
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}),
442
- "Private"
443
- ] }),
444
- r.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
445
- r.last ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 10.5, color: "var(--rm-quiet)" }, children: fmtTime(r.last.ts) }) : null
446
- ] })
447
- ]
448
- },
449
- r.roomId
450
- )),
451
- !mine.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-empty-hint", children: "No rooms in this workspace yet. Create one or join by invite \u2014 peers should check out the same project." }) : null
452
- ] })
453
- ] }),
454
- (s.workspaces || []).length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
455
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-group-label", children: "Workspaces" }),
456
- (s.workspaces || []).map((w) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-room-item" + (w.id === activeWs.id ? " is-active" : ""), onClick: () => a.setActiveWorkspace(w.id), children: [
366
+ className: "rm-icon-btn rm-ws-add",
367
+ title: `New room in ${wsTitle(ws)}`,
368
+ "aria-label": `New room in ${wsTitle(ws)}`,
369
+ onClick: () => a.openDialog({ kind: "create", workspace: { id: ws.workspaceId, path: ws.path, title: wsTitle(ws) } }),
370
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {})
371
+ }
372
+ )
373
+ ] }),
374
+ rooms.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomItem, { r, selected: r.roomId === s.selected, onClick: () => sel(r) }, r.roomId)),
375
+ !rooms.length && !qq ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-empty-hint rm-ws-empty", children: "No rooms \xB7 use + to create one here" }) : null
376
+ ] }, ws.workspaceId)),
377
+ other.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-group", children: [
378
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-head", children: [
457
379
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
458
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-room-item-body", children: [
459
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-name", children: w.name }),
460
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-meta", children: w.path })
461
- ] })
462
- ] }, w.id))
463
- ] }) : null,
464
- searching ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
465
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-group-label", children: [
466
- "Public directory ",
467
- s.search.loading ? "\xB7 searching\u2026" : ""
380
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-name", children: "Other rooms" })
468
381
  ] }),
469
- s.search.results.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-dir-item", "data-dir-room-id": r.roomId, children: [
470
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: r.name, size: 30 }),
471
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-dir-item-body", children: [
472
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-room-item-name", children: r.name }),
473
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-room-item-meta", children: [
474
- r.description || "No description",
475
- " \xB7 ",
476
- r.members,
477
- " online"
478
- ] }),
479
- (r.tags || []).length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-dir-tags", children: (r.tags || []).map((tg) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-tag", children: [
480
- "#",
481
- tg
482
- ] }, tg)) }) : null
483
- ] }),
484
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: r.joined ? "ghost" : "outline", disabled: r.joined || joining === r.roomId, onClick: () => join(r), children: r.joined ? "Joined" : joining === r.roomId ? "\u2026" : "Join" })
485
- ] }, r.roomId)),
486
- !s.search.loading && !s.search.results.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-empty-hint", children: s.search.error ? `Directory error: ${s.search.error}` : "No public rooms match." }) : null,
487
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-empty-hint", children: [
488
- "Joining attaches the room to workspace \u201C",
489
- activeWs.name,
490
- "\u201D. Point your local path at the same project."
491
- ] })
382
+ other.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomItem, { r, selected: r.roomId === s.selected, onClick: () => sel(r) }, r.roomId))
383
+ ] }) : null,
384
+ !workspaces.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-empty-hint", children: [
385
+ "No workspaces yet. Add one with the native \u201CAdd workspace\u201D in the left sidebar, then use \u201CNew room\u201D on it.",
386
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginTop: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: () => a.openDialog({ kind: "workspace" }), children: "Add workspace\u2026" }) })
492
387
  ] }) : null
493
388
  ] })
494
389
  ] });
495
390
  }
391
+ function RoomSessionMark({ store, sessionId }) {
392
+ const s = useStore(store);
393
+ const room = (s.rooms || []).find((r) => r.sessionId === sessionId);
394
+ if (!room) return null;
395
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-session-mark", title: `DSH Room \xB7 ${room.name}`, "aria-label": `DSH Room ${room.name}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsIcon, { size: 13 }) });
396
+ }
397
+ function WorkspaceSelect({ store, value, onChange, id }) {
398
+ const workspaces = useNativeWorkspaces(store);
399
+ (0, import_react.useEffect)(() => {
400
+ if (!value && workspaces[0]) onChange({ id: workspaces[0].workspaceId, path: workspaces[0].path, title: wsTitle(workspaces[0]) });
401
+ }, [workspaces.length]);
402
+ if (!workspaces.length) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "No dsh workspace yet \u2014 add one in the left sidebar (Add workspace)." });
403
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { id, className: "rm-input", value: value ? value.path : "", onChange: (e) => {
404
+ const w = workspaces.find((x) => x.path === e.target.value);
405
+ if (w) onChange({ id: w.workspaceId, path: w.path, title: wsTitle(w) });
406
+ }, children: workspaces.map((w) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", { value: w.path, children: [
407
+ wsTitle(w),
408
+ " \u2014 ",
409
+ w.path
410
+ ] }, w.workspaceId)) });
411
+ }
412
+ function renderMentions(text) {
413
+ return text.split(/(@[A-Za-z0-9][\w.:-]*)/g).map((part, i) => part.startsWith("@") ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mention", children: part }, i) : part);
414
+ }
415
+ function PanelIcon({ size }) {
416
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsIcon, { size });
417
+ }
496
418
  function Message({ m, showAuthor }) {
497
419
  if (m.kind === "task.create") {
498
420
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-msg rm-msg-event", "data-message-id": m.id, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-task-card", children: [
@@ -928,32 +850,26 @@ function RoomDetail({ room, messages, store, mention, assign }) {
928
850
  ] });
929
851
  }
930
852
  function EmptyMain({ store }) {
931
- const s = useStore(store);
932
853
  const a = store.actions;
933
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-root rm-welcome-wrap", "data-collab-main": "", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-welcome", children: [
854
+ const workspaces = useNativeWorkspaces(store);
855
+ const first = workspaces[0];
856
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-welcome-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-welcome", children: [
934
857
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-badge", children: "Collab \xB7 E2EE \xB7 no accounts" }),
935
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: "Workspaces, then rooms" }),
936
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "Bind a local workspace (project folder), then create rooms inside it. Peers join those rooms and point their agent at the same project. @mention or tasks wake agents. Relay or LAN transport; E2EE; optional public directory." }),
858
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: "Rooms live in your workspaces" }),
859
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "Your dsh workspaces stay in the left sidebar as usual. Hover a workspace there and click the room icon (next to \u201CNew session\u201D), or use its \u201C\u2026\u201D menu \u2192 New room. Peers join and point their agent at the same project; @mention or tasks wake agents." }),
937
860
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-welcome-actions", children: [
938
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "workspace" }), children: [
939
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
940
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
941
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Bind workspace" }),
942
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Local project path shared by agents in rooms" })
943
- ] })
944
- ] }),
945
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "create" }), children: [
861
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", disabled: !first, onClick: () => first && a.openDialog({ kind: "create", workspace: { id: first.workspaceId, path: first.path, title: wsTitle(first) } }), children: [
946
862
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}),
947
863
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
948
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Create room" }),
949
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "In the active workspace \xB7 Relay or LAN" })
864
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "New room" }),
865
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: first ? `In ${wsTitle(first)} \xB7 default relay 62.234.113.56:80` : "Add a workspace in the left sidebar first" })
950
866
  ] })
951
867
  ] }),
952
868
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "join" }), children: [
953
869
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkIcon, {}),
954
870
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
955
871
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Join by invite" }),
956
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Paste a collab://join/\u2026 invite" })
872
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Paste a collab://\u2026 invite" })
957
873
  ] })
958
874
  ] }),
959
875
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "browse" }), children: [
@@ -970,51 +886,60 @@ function RoomsMain({ store }) {
970
886
  const s = useStore(store);
971
887
  const [drafts, setDrafts] = (0, import_react.useState)({});
972
888
  const room = s.rooms.find((r) => r.roomId === s.selected);
973
- if (!room) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyMain, { store });
889
+ if (!room) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-main", "data-collab-main": "", children: [
890
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsNav, { store }),
891
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-main-body", children: [
892
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApprovalBanner, { store }),
893
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyMain, { store })
894
+ ] })
895
+ ] });
974
896
  const messages = s.messages[room.roomId] || [];
975
897
  const draft = drafts[room.roomId] || "";
976
898
  const setDraft = (v) => setDrafts((d) => ({ ...d, [room.roomId]: typeof v === "function" ? v(d[room.roomId] || "") : v }));
977
899
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-main", "data-collab-main": "", children: [
978
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApprovalBanner, { store }),
979
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-workspace", children: [
980
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "rm-chat", children: [
981
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "rm-chat-header", children: [
982
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: room.name, size: 28 }),
983
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
984
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-chat-header-title", children: room.name }),
985
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-chat-header-sub", children: [
986
- room.members.filter((m) => m.online !== false).length,
987
- " online \xB7 ",
988
- room.public ? "public" : "private",
989
- room.status !== "online" ? ` \xB7 room ${room.status}` : ""
990
- ] })
991
- ] }),
992
- room.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge rm-badge-public", children: [
993
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
994
- "Public"
995
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge", children: [
996
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}),
997
- "Private"
900
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsNav, { store }),
901
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-main-body", children: [
902
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApprovalBanner, { store }),
903
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-workspace", children: [
904
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "rm-chat", children: [
905
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "rm-chat-header", children: [
906
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: room.name, size: 28 }),
907
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
908
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-chat-header-title", children: room.name }),
909
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-chat-header-sub", children: [
910
+ room.members.filter((m) => m.online !== false).length,
911
+ " online \xB7 ",
912
+ room.public ? "public" : "private",
913
+ room.status !== "online" ? ` \xB7 room ${room.status}` : ""
914
+ ] })
915
+ ] }),
916
+ room.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge rm-badge-public", children: [
917
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
918
+ "Public"
919
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge", children: [
920
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}),
921
+ "Private"
922
+ ] }),
923
+ room.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
924
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: room.status === "online" ? "done" : room.status === "connecting" ? "ongoing" : "error", size: 8 })
998
925
  ] }),
999
- room.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
1000
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: room.status === "online" ? "done" : room.status === "connecting" ? "ongoing" : "error", size: 8 })
926
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages }),
927
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Composer, { room, store, draft, setDraft })
1001
928
  ] }),
1002
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages }),
1003
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Composer, { room, store, draft, setDraft })
1004
- ] }),
1005
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1006
- RoomDetail,
1007
- {
1008
- room,
1009
- messages,
1010
- store,
1011
- mention: (id) => setDraft((d) => `${d}${d && !d.endsWith(" ") ? " " : ""}@${id} `),
1012
- assign: (id) => {
1013
- setDraft(`@${id} `);
1014
- store.actions.toast(`Switch to task mode, then describe the task for @${id}`);
929
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
930
+ RoomDetail,
931
+ {
932
+ room,
933
+ messages,
934
+ store,
935
+ mention: (id) => setDraft((d) => `${d}${d && !d.endsWith(" ") ? " " : ""}@${id} `),
936
+ assign: (id) => {
937
+ setDraft(`@${id} `);
938
+ store.actions.toast(`Switch to task mode, then describe the task for @${id}`);
939
+ }
1015
940
  }
1016
- }
1017
- )
941
+ )
942
+ ] })
1018
943
  ] })
1019
944
  ] });
1020
945
  }
@@ -1025,7 +950,7 @@ function Field({ label, hint, children, htmlFor }) {
1025
950
  hint ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: hint }) : null
1026
951
  ] });
1027
952
  }
1028
- function CreateDialog({ store }) {
953
+ function CreateDialog({ store, d }) {
1029
954
  const s = useStore(store);
1030
955
  const a = store.actions;
1031
956
  const [f, setF] = (0, import_react.useState)({ name: "", description: "", tags: "", public: false, mode: "lan", lanHostPort: "62.234.113.56:80" });
@@ -1033,9 +958,13 @@ function CreateDialog({ store }) {
1033
958
  const [busy, setBusy] = (0, import_react.useState)(false);
1034
959
  const [lanPeers, setLanPeers] = (0, import_react.useState)([]);
1035
960
  const ok = f.name.trim() && !busy;
1036
- const ws = (s.workspaces || []).find((w) => w.id === s.activeWorkspaceId) || (s.workspaces || [])[0];
961
+ const [ws, setWs] = (0, import_react.useState)(d && d.workspace ? d.workspace : null);
1037
962
  const submit = async () => {
1038
963
  if (!ok) return;
964
+ if (!ws) {
965
+ setErr("Pick a workspace");
966
+ return;
967
+ }
1039
968
  setBusy(true);
1040
969
  setErr(null);
1041
970
  try {
@@ -1045,7 +974,9 @@ function CreateDialog({ store }) {
1045
974
  tags: f.tags,
1046
975
  public: f.public,
1047
976
  mode: f.mode,
1048
- lanHostPort: f.mode === "lan" ? f.lanHostPort.trim() : void 0
977
+ lanHostPort: f.mode === "lan" ? f.lanHostPort.trim() : void 0,
978
+ workspacePath: ws.path,
979
+ workspaceName: ws.title
1049
980
  });
1050
981
  } catch (e) {
1051
982
  setErr(e.message);
@@ -1068,12 +999,13 @@ function CreateDialog({ store }) {
1068
999
  title: "Create room",
1069
1000
  closeLabel: "Close",
1070
1001
  className: "rm-modal-wide",
1071
- description: ws ? `In workspace \u201C${ws.name}\u201D. Default relay 62.234.113.56:80 \xB7 E2EE \xB7 keys stay local.` : "Bind a workspace first. Default relay 62.234.113.56:80.",
1002
+ description: ws ? `In workspace \u201C${ws.title}\u201D (${ws.path}). Agent cwd + MCP sandbox = this path. Default relay 62.234.113.56:80 \xB7 E2EE.` : "Pick a dsh workspace. Default relay 62.234.113.56:80 \xB7 E2EE.",
1072
1003
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1073
1004
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
1074
1005
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: !ok, onClick: submit, children: busy ? "Creating\u2026" : "Create" })
1075
1006
  ] }),
1076
1007
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form rm-form-wide", onKeyDown: (e) => e.key === "Enter" && e.target.tagName === "INPUT" && submit(), children: [
1008
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Workspace", htmlFor: "rm-create-ws", hint: "Native dsh workspace this room belongs to.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceSelect, { store, id: "rm-create-ws", value: ws, onChange: setWs }) }),
1077
1009
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Name", htmlFor: "rm-create-name", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-name-row", children: [
1078
1010
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: f.name || "room", size: 36 }),
1079
1011
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-name", autoFocus: true, placeholder: "e.g. release-crew", maxLength: 60, value: f.name, onChange: (e) => setF({ ...f, name: e.target.value }) })
@@ -1113,12 +1045,13 @@ function JoinDialog({ store }) {
1113
1045
  const [err, setErr] = (0, import_react.useState)(null);
1114
1046
  const [busy, setBusy] = (0, import_react.useState)(false);
1115
1047
  const valid = /^collab:\/\/(join|lan|relay)\/.+#\S+$/.test(invite.trim());
1048
+ const [ws, setWs] = (0, import_react.useState)(null);
1116
1049
  const submit = async () => {
1117
1050
  if (!valid || busy) return;
1118
1051
  setBusy(true);
1119
1052
  setErr(null);
1120
1053
  try {
1121
- await a.join(invite.trim());
1054
+ await a.join(invite.trim(), ws ? { workspacePath: ws.path, workspaceName: ws.title } : void 0);
1122
1055
  } catch (e) {
1123
1056
  setErr(e.message);
1124
1057
  setBusy(false);
@@ -1131,12 +1064,13 @@ function JoinDialog({ store }) {
1131
1064
  onClose: a.closeDialog,
1132
1065
  title: "Join room",
1133
1066
  closeLabel: "Close",
1134
- description: "Paste an invite. The room attaches to your active workspace \u2014 set the local path to your checkout of the same project before collaborating via MCP.",
1067
+ description: "Paste an invite and pick the dsh workspace (your checkout of the same project) the room attaches to.",
1135
1068
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1136
1069
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
1137
1070
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: !valid || busy, onClick: submit, children: busy ? "Joining\u2026" : "Join" })
1138
1071
  ] }),
1139
1072
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form", children: [
1073
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceSelect, { store, value: ws, onChange: setWs }),
1140
1074
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { autoFocus: true, "aria-label": "Invite", placeholder: "collab://join/\u2026 or collab://lan/<host>:<port>/<roomId>#\u2026", value: invite, onChange: (e) => setInvite(e.target.value), onKeyDown: (e) => e.key === "Enter" && submit() }),
1141
1075
  invite && !valid ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Invites: collab://join/\u2026#key, collab://lan/host:port/room#key, or collab://relay/host/join/room#key" }) : null,
1142
1076
  err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: err }) : null
@@ -1326,9 +1260,10 @@ function WorkspaceDialog({ store }) {
1326
1260
  setBusy(true);
1327
1261
  setErr(null);
1328
1262
  try {
1329
- await a.ensureWorkspace({ name: name2.trim() || void 0, path: path.trim() });
1263
+ if (store.native && store.native.create) await store.native.create({ path: path.trim() });
1264
+ else await a.ensureWorkspace({ name: name2.trim() || void 0, path: path.trim() });
1330
1265
  a.closeDialog();
1331
- a.toast("Workspace ready \u2014 create a room to collaborate");
1266
+ a.toast("Workspace added \u2014 it appears in the left sidebar; use New room on it");
1332
1267
  } catch (e) {
1333
1268
  setErr(e.message);
1334
1269
  setBusy(false);
@@ -1339,18 +1274,14 @@ function WorkspaceDialog({ store }) {
1339
1274
  {
1340
1275
  open: true,
1341
1276
  onClose: a.closeDialog,
1342
- title: "Workspace",
1277
+ title: "Add workspace",
1343
1278
  closeLabel: "Close",
1344
- description: "Pick the local project folder agents share. Rooms created next inherit this path (MCP + agent cwd). Peers map the same logical project to their own checkout.",
1279
+ description: "Fallback: registers a dsh workspace (same as the native \u201CAdd workspace\u201D). It shows up in the left sidebar.",
1345
1280
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1346
1281
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
1347
1282
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: busy || !path.trim(), onClick: submit, children: busy ? "Saving\u2026" : "Use workspace" })
1348
1283
  ] }),
1349
1284
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form", children: [
1350
- (s.workspaces || []).length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Switch to existing", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-lan-browse", children: (s.workspaces || []).map((w) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-lan-peer", onClick: async () => {
1351
- await a.setActiveWorkspace(w.id);
1352
- a.closeDialog();
1353
- }, children: w.name }, w.id)) }) }) : null,
1354
1285
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Name", htmlFor: "rm-ws-name", hint: "Shown in the sidebar.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-ws-name", placeholder: "e.g. collab", value: name2, onChange: (e) => setName(e.target.value) }) }),
1355
1286
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Local path", htmlFor: "rm-ws-path", hint: "Absolute path on this machine.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-ws-path", autoFocus: true, placeholder: "/Users/you/code/my-app", value: path, onChange: (e) => setPath(e.target.value), onKeyDown: (e) => e.key === "Enter" && submit() }) }),
1356
1287
  err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: err }) : null
@@ -1363,7 +1294,7 @@ function BrowseDialog({ store }) {
1363
1294
  const a = store.actions;
1364
1295
  const [joining, setJoining] = (0, import_react.useState)(null);
1365
1296
  const inputRef = (0, import_react.useRef)(null);
1366
- const activeWs = (s.workspaces || []).find((w) => w.id === s.activeWorkspaceId) || (s.workspaces || [])[0];
1297
+ const [activeWs, setActiveWs] = (0, import_react.useState)(null);
1367
1298
  (0, import_react.useEffect)(() => {
1368
1299
  a.setSearchOpen(true);
1369
1300
  requestAnimationFrame(() => inputRef.current?.focus());
@@ -1373,7 +1304,7 @@ function BrowseDialog({ store }) {
1373
1304
  const join = async (r) => {
1374
1305
  setJoining(r.roomId);
1375
1306
  try {
1376
- await a.join(r.invite, { name: r.name, description: r.description, tags: r.tags, public: true, workspaceId: activeWs?.id });
1307
+ await a.join(r.invite, { name: r.name, description: r.description, tags: r.tags, public: true, workspacePath: activeWs?.path, workspaceName: activeWs?.title });
1377
1308
  } catch (e) {
1378
1309
  a.toast(e.message);
1379
1310
  } finally {
@@ -1388,9 +1319,10 @@ function BrowseDialog({ store }) {
1388
1319
  title: "Browse public rooms",
1389
1320
  closeLabel: "Close",
1390
1321
  className: "rm-modal-wide",
1391
- description: "Directory on VPS 62.234.113.56:80. Search by name or #tag, then join into the active workspace.",
1322
+ description: "Directory on VPS 62.234.113.56:80. Search by name or #tag; joined rooms attach to the workspace below.",
1392
1323
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Close" }),
1393
1324
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-browse", children: [
1325
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceSelect, { store, value: activeWs, onChange: setActiveWs }),
1394
1326
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search rm-browse-search", children: [
1395
1327
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
1396
1328
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1434,7 +1366,7 @@ function Overlay({ store }) {
1434
1366
  const s = useStore(store);
1435
1367
  const d = s.dialog;
1436
1368
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1437
- d?.kind === "create" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CreateDialog, { store }) : null,
1369
+ d?.kind === "create" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CreateDialog, { store, d }) : null,
1438
1370
  d?.kind === "join" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JoinDialog, { store }) : null,
1439
1371
  d?.kind === "invite" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InviteDialog, { store, d }) : null,
1440
1372
  d?.kind === "leave" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LeaveDialog, { store, d }) : null,
@@ -1453,8 +1385,118 @@ function installStyles() {
1453
1385
  document.head.appendChild(style);
1454
1386
  return () => style.remove();
1455
1387
  }
1388
+ var ROOM_SVG = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.15" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2.5" y="3" width="11" height="10" rx="2"/><path d="M5.5 6.5h5M5.5 9.5h3.5"/></svg>';
1389
+ function installWorkspaceDecorator(store, native, onNewRoom) {
1390
+ if (typeof document === "undefined" || !native) return () => {
1391
+ };
1392
+ const wsFor = (key) => (native.list.getSnapshot().items || []).find((w) => w.workspaceId === key);
1393
+ const rowKey = (row) => (row.getAttribute("data-row-key") || "").slice("workspace:".length);
1394
+ const open = (key) => {
1395
+ const w = wsFor(key);
1396
+ if (w) onNewRoom({ id: w.workspaceId, path: w.path, title: wsTitle(w) });
1397
+ };
1398
+ const decorate = () => {
1399
+ for (const row of document.querySelectorAll('[data-row-key^="workspace:"]')) {
1400
+ const key = rowKey(row);
1401
+ if (!key || row.querySelector("[data-rm-ws-new-room]")) continue;
1402
+ const newSess = row.querySelector('button[aria-label^="New session in"]');
1403
+ if (!newSess) continue;
1404
+ let anchor = newSess;
1405
+ while (anchor.parentElement && anchor.parentElement !== row && !anchor.parentElement.querySelector('button[aria-label^="Workspace actions"]')) anchor = anchor.parentElement;
1406
+ if (!anchor.parentElement) continue;
1407
+ const label = (newSess.getAttribute("aria-label") || "").replace(/^New session in /, "");
1408
+ const btn = document.createElement("button");
1409
+ btn.type = "button";
1410
+ btn.className = newSess.className;
1411
+ btn.setAttribute("data-rm-ws-new-room", key);
1412
+ btn.setAttribute("aria-label", `New room in ${label}`);
1413
+ btn.title = "New room";
1414
+ btn.innerHTML = ROOM_SVG;
1415
+ btn.addEventListener("click", (e) => {
1416
+ e.preventDefault();
1417
+ e.stopPropagation();
1418
+ open(key);
1419
+ });
1420
+ anchor.parentElement.insertBefore(btn, anchor);
1421
+ }
1422
+ };
1423
+ let pendingKey = null;
1424
+ const onClickCapture = (e) => {
1425
+ const trigger = e.target instanceof Element ? e.target.closest('button[aria-label^="Workspace actions for"]') : null;
1426
+ if (!trigger) return;
1427
+ const row = trigger.closest('[data-row-key^="workspace:"]');
1428
+ pendingKey = row ? rowKey(row) : null;
1429
+ if (!pendingKey) return;
1430
+ const key = pendingKey;
1431
+ let tries = 0;
1432
+ const augment = () => {
1433
+ const menus = [...document.querySelectorAll('[role="menu"]')].filter((m) => m.querySelector('[role="menuitem"]'));
1434
+ const menu = menus[menus.length - 1];
1435
+ if (!menu) {
1436
+ if (++tries < 10) requestAnimationFrame(augment);
1437
+ return;
1438
+ }
1439
+ if (menu.querySelector("[data-rm-ws-menu]")) return;
1440
+ const tpl = menu.querySelector('[role="menuitem"]');
1441
+ const mk = (text, svg, onPick) => {
1442
+ const item = document.createElement("button");
1443
+ item.type = "button";
1444
+ item.setAttribute("role", "menuitem");
1445
+ item.setAttribute("data-rm-ws-menu", text);
1446
+ item.className = tpl.className;
1447
+ item.innerHTML = `<span style="display:inline-flex;width:16px;justify-content:center">${svg}</span><span>${text}</span>`;
1448
+ item.style.display = "flex";
1449
+ item.style.alignItems = "center";
1450
+ item.style.gap = "8px";
1451
+ item.addEventListener("click", (ev) => {
1452
+ ev.preventDefault();
1453
+ ev.stopPropagation();
1454
+ trigger.click();
1455
+ onPick();
1456
+ });
1457
+ return item;
1458
+ };
1459
+ const newSessSvg = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.15" stroke-linecap="round" aria-hidden="true"><path d="M8 3.5v9M3.5 8h9"/></svg>';
1460
+ const first = menu.firstElementChild;
1461
+ menu.insertBefore(mk("New session", newSessSvg, () => {
1462
+ const row2 = document.querySelector(`[data-row-key="workspace:${CSS.escape(key)}"]`);
1463
+ row2?.querySelector('button[aria-label^="New session in"]')?.click();
1464
+ }), first);
1465
+ menu.insertBefore(mk("New room", ROOM_SVG, () => open(key)), first);
1466
+ };
1467
+ requestAnimationFrame(augment);
1468
+ };
1469
+ let raf = 0;
1470
+ const schedule = () => {
1471
+ if (!raf) raf = requestAnimationFrame(() => {
1472
+ raf = 0;
1473
+ decorate();
1474
+ });
1475
+ };
1476
+ const mo = new MutationObserver(schedule);
1477
+ mo.observe(document.body, { childList: true, subtree: true });
1478
+ document.addEventListener("click", onClickCapture, true);
1479
+ schedule();
1480
+ return () => {
1481
+ mo.disconnect();
1482
+ document.removeEventListener("click", onClickCapture, true);
1483
+ if (raf) cancelAnimationFrame(raf);
1484
+ for (const b of document.querySelectorAll("[data-rm-ws-new-room]")) b.remove();
1485
+ };
1486
+ }
1456
1487
  function apply(ctx) {
1457
1488
  const store = createRoomsStore();
1489
+ let native = null;
1490
+ try {
1491
+ native = ctx.get ? ctx.get("workspaces") : ctx.workspaces;
1492
+ } catch {
1493
+ native = ctx.workspaces || null;
1494
+ }
1495
+ store.native = native || null;
1496
+ store.nativeList = native && native.list ? {
1497
+ subscribe: (listener) => native.list.subscribe(listener),
1498
+ getSnapshot: () => native.list.getSnapshot()
1499
+ } : null;
1458
1500
  ctx.effect(() => installStyles(), "dsh-room: styles");
1459
1501
  ctx.effect(() => () => store.dispose(), "dsh-room: store");
1460
1502
  const info = () => ctx.layout.panelInfo;
@@ -1464,27 +1506,12 @@ function apply(ctx) {
1464
1506
  } catch {
1465
1507
  }
1466
1508
  };
1509
+ store.onRoomOpened = ensurePanel;
1467
1510
  ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({ name: "sidebar.panellist", id: PANEL_ID, order: 11, label: () => "DSH Room" }, PanelIcon));
1468
1511
  ctx.slots.inject("main", () => ctx.slots.register({ name: "main", key: PANEL_ID, inject: () => ({ store }) }, RoomsMain));
1469
1512
  ctx.slots.inject("shell.overlay", () => ctx.slots.register({ name: "shell.overlay", id: "dsh-room-dialogs", inject: () => ({ store }) }, Overlay));
1470
- ctx.slots.inject("sidebar.workspaces", () => {
1471
- let dispose;
1472
- const reconcile = () => {
1473
- const active = info().getSnapshot().activePanelId === PANEL_ID;
1474
- if (active && !dispose) dispose = ctx.slots.register({ name: "sidebar.workspaces", priority: -100, inject: () => ({ store, ensurePanel }) }, RoomsSidebar);
1475
- else if (!active && dispose) {
1476
- dispose();
1477
- dispose = void 0;
1478
- }
1479
- };
1480
- const off = info().subscribe(reconcile);
1481
- reconcile();
1482
- return () => {
1483
- off();
1484
- if (dispose) dispose();
1485
- dispose = void 0;
1486
- };
1487
- });
1513
+ ctx.slots.inject("sidebar.session.row.leading", () => ctx.slots.register({ name: "sidebar.session.row.leading", id: "dsh-room-session-mark", inject: () => ({ store }) }, RoomSessionMark));
1514
+ ctx.effect(() => installWorkspaceDecorator(store, native, (workspace) => store.actions.openDialog({ kind: "create", workspace })), "dsh-room: workspace New room");
1488
1515
  }
1489
1516
 
1490
1517
  return module.exports;
@@ -1528,7 +1555,7 @@ var import_react = require("react");
1528
1555
  var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
1529
1556
 
1530
1557
  // src/client/rooms.css
1531
- var rooms_default = '/* Collab Rooms UI \u2014 original surface inside the dsh shell. Uses only dsh --dsw-* tokens.\n Independent Rooms surface. Prefix: rm- (rooms). */\n\n.rm-root {\n --rm-accent: var(--dsw-alias-state-business-primary);\n --rm-hover: var(--dsw-alias-interactive-bg-hover);\n --rm-border: var(--dsw-alias-border-l2);\n --rm-bg: var(--dsw-alias-bg-base);\n --rm-panel: var(--dsw-alias-bg-module-platform);\n --rm-label: var(--dsw-alias-label-primary);\n --rm-muted: var(--dsw-alias-label-secondary);\n --rm-quiet: var(--dsw-alias-label-tertiary);\n font: 13px/1.5 var(--dsw-font-family);\n color: var(--rm-label);\n box-sizing: border-box;\n}\n.rm-root *, .rm-root *::before, .rm-root *::after { box-sizing: border-box; }\n.rm-root button, .rm-root input, .rm-root textarea { font: inherit; }\n\n/* \u2500\u2500 sidebar (joined rooms + public search) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-side {\n display: flex; flex-direction: column; height: 100%; min-height: 0;\n padding: 0 8px 8px 0; gap: 8px;\n}\n.rm-side-toolbar {\n display: flex; align-items: center; gap: 6px; flex: none; padding: 4px 0 2px;\n}\n.rm-side-title {\n flex: 1; font-size: 12px; font-weight: 600; letter-spacing: .04em;\n text-transform: uppercase; color: var(--rm-quiet);\n}\n.rm-icon-btn {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; padding: 0;\n}\n.rm-icon-btn:hover { background: var(--rm-hover); color: var(--rm-label); }\n.rm-search {\n display: flex; align-items: center; gap: 6px; height: 32px; padding: 0 10px;\n border: .5px solid var(--rm-border); border-radius: 8px; background: var(--rm-panel);\n}\n.rm-search input {\n flex: 1; min-width: 0; border: 0; background: transparent; outline: none; color: var(--rm-label); font-size: 13px;\n}\n.rm-search input::placeholder { color: var(--rm-quiet); }\n.rm-side-scroll { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 12px; }\n.rm-group-label { font-size: 11px; font-weight: 600; color: var(--rm-quiet); padding: 0 4px 4px; text-transform: uppercase; letter-spacing: .03em; }\n.rm-room-list { display: flex; flex-direction: column; gap: 2px; }\n.rm-room-item {\n display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px;\n border: 0; border-radius: 10px; background: transparent; color: inherit; cursor: pointer; text-align: left;\n}\n.rm-room-item:hover { background: var(--rm-hover); }\n.rm-room-item[aria-current="true"] { background: var(--rm-hover); }\n.rm-room-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }\n.rm-room-item-name { font-weight: 600; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-meta { font-size: 11.5px; color: var(--rm-quiet); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-trail { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }\n.rm-badge {\n display: inline-flex; align-items: center; gap: 3px; height: 18px; padding: 0 6px;\n border-radius: 999px; font-size: 10.5px; font-weight: 500; background: var(--rm-panel); color: var(--rm-muted);\n border: .5px solid var(--rm-border);\n}\n.rm-badge-public { color: var(--rm-accent); border-color: color-mix(in srgb, var(--rm-accent) 35%, transparent); background: color-mix(in srgb, var(--rm-accent) 8%, transparent); }\n.rm-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--rm-quiet); }\n.rm-dot-online { background: var(--dsw-alias-state-success-primary, #2bb673); }\n.rm-dot-connecting { background: var(--dsw-alias-state-warn-primary, #e9a23b); }\n.rm-dot-offline { background: var(--dsw-alias-state-error-primary, #e5484d); }\n.rm-empty-hint { padding: 8px 10px; font-size: 12px; color: var(--rm-quiet); line-height: 1.45; }\n.rm-dir-item {\n display: flex; align-items: flex-start; gap: 10px; padding: 10px; border-radius: 10px;\n border: .5px solid var(--rm-border); background: var(--rm-bg); margin-bottom: 6px;\n}\n.rm-dir-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }\n.rm-dir-tags { display: flex; flex-wrap: wrap; gap: 4px; }\n.rm-tag { font-size: 11px; color: var(--rm-muted); background: var(--rm-hover); border-radius: 4px; padding: 0 5px; }\n.rm-menu {\n position: fixed; z-index: 1000; min-width: 180px; padding: 4px;\n border-radius: 10px; background: var(--dsw-alias-bg-layer-1, var(--rm-bg));\n border: .5px solid var(--rm-border);\n box-shadow: 0 8px 24px color-mix(in srgb, var(--rm-label) 12%, transparent);\n display: flex; flex-direction: column;\n}\n.rm-menu button {\n display: flex; align-items: center; gap: 8px; height: 32px; padding: 0 10px;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-menu button:hover { background: var(--rm-hover); }\n\n/* \u2500\u2500 main: room workspace \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-main { display: flex; flex: 1; min-height: 0; height: 100%; width: 100%; background: var(--rm-bg); }\n.rm-workspace { display: flex; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat-header {\n flex: none; display: flex; align-items: center; gap: 10px; height: 48px;\n padding: 0 16px; border-bottom: .5px solid var(--rm-border);\n}\n.rm-chat-header-title { font-size: 14px; font-weight: 600; }\n.rm-chat-header-sub { font-size: 12px; color: var(--rm-quiet); }\n.rm-messages {\n flex: 1; min-height: 0; overflow: auto; padding: 16px 18px 8px;\n display: flex; flex-direction: column; gap: 2px;\n}\n.rm-day {\n align-self: center; margin: 10px 0 12px; padding: 2px 10px; border-radius: 999px;\n font-size: 11px; color: var(--rm-quiet); background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n}\n.rm-msg {\n display: grid; grid-template-columns: 32px 1fr; gap: 0 10px; padding: 4px 6px; border-radius: 8px;\n max-width: 720px;\n}\n.rm-msg:hover { background: color-mix(in srgb, var(--rm-hover) 60%, transparent); }\n.rm-msg-self { margin-left: auto; grid-template-columns: 1fr; max-width: 640px; }\n.rm-msg-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 1px; }\n.rm-msg-author { font-size: 12px; font-weight: 600; color: var(--rm-muted); }\n.rm-msg-time { font-size: 11px; color: var(--rm-quiet); }\n.rm-msg-body { font-size: 13.5px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; }\n.rm-msg-self .rm-msg-body {\n background: var(--dsw-alias-label-primary); color: var(--dsw-alias-label-primary-foreground);\n padding: 8px 12px; border-radius: 12px 12px 4px 12px; width: fit-content; margin-left: auto;\n}\n.rm-msg-event {\n grid-template-columns: 1fr; max-width: 560px; margin: 6px 0;\n padding: 8px 12px; border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-panel);\n}\n.rm-mention { color: var(--rm-accent); font-weight: 500; }\n.rm-msg-self .rm-mention { color: inherit; text-decoration: underline; text-underline-offset: 2px; }\n.rm-task-card { display: flex; flex-direction: column; gap: 4px; }\n.rm-task-head { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--rm-muted); }\n.rm-task-title { font-weight: 600; font-size: 13.5px; }\n.rm-composer {\n flex: none; display: flex; align-items: flex-end; gap: 8px; padding: 10px 16px 14px;\n border-top: .5px solid var(--rm-border);\n}\n.rm-composer-box {\n flex: 1; display: flex; align-items: flex-end; gap: 6px; min-height: 44px;\n padding: 8px 10px; border: .5px solid var(--rm-border); border-radius: 12px; background: var(--rm-panel);\n}\n.rm-composer-box:focus-within { border-color: color-mix(in srgb, var(--rm-accent) 50%, var(--rm-border)); }\n.rm-composer-box textarea {\n flex: 1; min-height: 28px; max-height: 120px; border: 0; background: transparent; outline: none;\n resize: none; color: var(--rm-label); line-height: 1.45;\n}\n.rm-composer-box textarea::placeholder { color: var(--rm-quiet); }\n.rm-composer-mode {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; flex: none;\n}\n.rm-composer-mode:hover, .rm-composer-mode[aria-pressed="true"] {\n color: var(--rm-accent); background: color-mix(in srgb, var(--rm-accent) 12%, transparent);\n}\n.rm-chat-hint { margin: 48px auto; text-align: center; color: var(--rm-quiet); font-size: 13px; max-width: 360px; line-height: 1.5; }\n\n/* \u2500\u2500 detail column (members / invite / about) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-detail {\n flex: 0 0 280px; width: 280px; display: flex; flex-direction: column; min-height: 0;\n border-left: .5px solid var(--rm-border); background: var(--rm-bg); overflow: auto;\n}\n.rm-detail-section { padding: 14px 14px 8px; border-bottom: .5px solid var(--rm-border); }\n.rm-detail-section:last-child { border-bottom: 0; }\n.rm-detail-h {\n display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600;\n text-transform: uppercase; letter-spacing: .04em; color: var(--rm-quiet); margin-bottom: 8px;\n}\n.rm-member {\n display: flex; align-items: center; gap: 8px; padding: 5px 4px; border-radius: 8px; min-height: 34px;\n}\n.rm-member:hover { background: var(--rm-hover); }\n.rm-member-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }\n.rm-member-actions { display: none; gap: 2px; }\n.rm-member:hover .rm-member-actions, .rm-member:focus-within .rm-member-actions { display: inline-flex; }\n.rm-mini {\n width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-muted); cursor: pointer; font-size: 12px;\n}\n.rm-mini:hover { background: var(--dsw-alias-interactive-bg-active); color: var(--rm-label); }\n.rm-task-row { display: flex; align-items: center; gap: 6px; padding: 4px; font-size: 12.5px; }\n.rm-task-row-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-about { display: grid; grid-template-columns: 88px 1fr; gap: 6px 8px; font-size: 12.5px; margin: 0; }\n.rm-about dt { color: var(--rm-quiet); margin: 0; }\n.rm-about dd { margin: 0; min-width: 0; overflow-wrap: anywhere; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }\n.rm-mono { font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace); font-size: 11.5px; }\n.rm-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }\n.rm-danger { color: var(--dsw-alias-state-error-primary, #e5484d) !important; }\n.rm-note { font-size: 12px; color: var(--rm-quiet); line-height: 1.45; margin: 0 0 8px; }\n\n/* \u2500\u2500 empty / welcome \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-welcome-wrap {\n flex: 1; display: flex; align-items: center; justify-content: center; padding: 32px; background: var(--rm-bg);\n}\n.rm-welcome {\n width: min(420px, 100%); display: flex; flex-direction: column; gap: 10px;\n padding: 20px; border-radius: 14px; border: .5px solid var(--rm-border); background: var(--rm-panel);\n}\n.rm-welcome h1 { margin: 0; font-size: 16px; font-weight: 600; }\n.rm-welcome p { margin: 0; font-size: 13px; color: var(--rm-muted); line-height: 1.5; }\n.rm-welcome-actions { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }\n.rm-welcome-action {\n display: flex; align-items: flex-start; gap: 10px; width: 100%; padding: 10px 12px;\n border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-bg);\n color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-welcome-action:hover { background: var(--rm-hover); }\n.rm-welcome-action b { display: block; font-size: 13px; }\n.rm-welcome-action span { display: block; font-size: 12px; color: var(--rm-quiet); margin-top: 2px; }\n\n/* \u2500\u2500 avatars \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-avatar {\n display: inline-flex; align-items: center; justify-content: center; flex: none;\n font-weight: 600; letter-spacing: .02em; user-select: none;\n}\n.rm-avatar-wrap { position: relative; display: inline-flex; flex: none; }\n\n/* \u2500\u2500 dialogs (inside dsh Modal) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-form { display: flex; flex-direction: column; gap: 14px; }\n.rm-field { display: flex; flex-direction: column; gap: 6px; }\n.rm-field-label { font-size: 13px; font-weight: 500; }\n.rm-field-hint { font-size: 12px; color: var(--rm-quiet); }\n.rm-name-row { display: flex; align-items: center; gap: 10px; }\n.rm-name-row > :last-child { flex: 1; }\n.rm-switch-row {\n display: flex; align-items: center; justify-content: space-between; gap: 14px;\n padding: 10px 12px; border-radius: 10px; background: var(--rm-panel);\n}\n.rm-error { color: var(--dsw-alias-state-error-primary, #e5484d); font-size: 12.5px; }\n.rm-invite {\n display: block; padding: 10px 12px; border-radius: 8px; background: var(--rm-panel);\n font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace);\n font-size: 12px; word-break: break-all; user-select: all;\n}\n\n/* Dual transport + approvals + agent config */\n.rm-mode-row { display: flex; gap: 8px; }\n.rm-mode-btn {\n flex: 1; padding: 10px 12px; border-radius: 10px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer; font: inherit;\n}\n.rm-mode-btn.is-on { border-color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent); }\n.rm-lan-browse { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; align-items: center; }\n.rm-lan-peer {\n font-size: 12px; padding: 4px 8px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer;\n}\n.rm-approvals { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); background: color-mix(in srgb, #f5a524 12%, transparent); }\n.rm-approval { display: flex; gap: 12px; align-items: center; justify-content: space-between; }\n.rm-approval-body { font-size: 13px; }\n.rm-approval-actions { display: flex; gap: 6px; flex-shrink: 0; }\n.rm-input { width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; font: inherit; }\n\n.rm-ws-bar { display: flex; flex-direction: column; gap: 4px; padding: 8px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); }\n.rm-ws-chip { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; cursor: pointer; font: inherit; font-size: 12.5px; }\n.rm-ws-path { font-size: 11px; color: var(--rm-quiet, #8b93a7); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item.is-active { background: color-mix(in srgb, var(--rm-accent, #5b8cff) 12%, transparent); }\n\n/* Native-aligned room composer (mirrors dsh conversation control row) */\n.rm-composer-card {\n box-sizing: border-box;\n width: 100%;\n max-width: 920px;\n margin: 0 auto;\n border-radius: 16px;\n background: var(--rm-elev, var(--rm-panel, #12151c));\n border: 1px solid var(--rm-border, #2a2f3a);\n box-shadow: 0 1px 2px rgba(0,0,0,.06);\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 8px 0 6px;\n}\n.rm-composer-grow { position: relative; padding: 0 4px; }\n.rm-composer-grow textarea {\n box-sizing: border-box; width: 100%; min-height: 36px; max-height: 160px;\n resize: none; border: none; outline: none; background: transparent;\n color: inherit; font: inherit; font-size: 14px; line-height: 22px;\n padding: 4px 12px 0 14px;\n}\n.rm-composer-grow textarea:disabled { opacity: .55; cursor: not-allowed; }\n.rm-composer-row {\n display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;\n gap: 10px; min-width: 0; padding: 2px 8px 4px;\n}\n.rm-composer-tools, .rm-composer-modes, .rm-composer-trailing {\n display: flex; align-items: center; min-width: 0; gap: 8px;\n}\n.rm-composer-trailing { flex: none; margin-left: auto; }\n.rm-composer-add {\n width: 28px; height: 28px; border: none; border-radius: 999px;\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n color: inherit; cursor: pointer; display: grid; place-items: center;\n}\n.rm-composer-add:hover, .rm-composer-add[aria-pressed="true"] {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 22%, transparent);\n}\n.rm-composer-select {\n max-width: 220px; height: 28px; border: none; border-radius: 8px;\n background: transparent; color: var(--rm-quiet, #8b93a7);\n font: inherit; font-size: 13px; font-weight: 500; line-height: 20px;\n padding: 0 8px; cursor: pointer; white-space: nowrap;\n}\n.rm-composer-select:hover:not(:disabled) {\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n}\n.rm-composer-select:disabled { opacity: .5; cursor: default; }\n.rm-composer-model-text { display: inline; }\n.rm-composer-model-icon { display: none; font-size: 10px; }\n.rm-composer-effort {\n margin-left: 6px; font-size: 11px; opacity: .75;\n text-transform: uppercase; letter-spacing: .02em;\n}\n.rm-composer-send {\n width: 34px; height: 34px; border: none; border-radius: 999px;\n background: var(--rm-accent, #5b8cff); color: #fff; cursor: pointer;\n display: grid; place-items: center; transform: translateY(-2px);\n}\n.rm-composer-send:hover:not(:disabled) { filter: brightness(1.08); }\n.rm-composer-send:disabled { opacity: .4; cursor: default; }\n.rm-composer-popwrap { position: relative; }\n.rm-composer-menu {\n position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 40;\n min-width: 200px; max-height: 320px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 8px 24px rgba(0,0,0,.28);\n padding: 6px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-composer-menu-wide { min-width: 280px; max-width: 340px; }\n.rm-composer-menu-label {\n font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;\n color: var(--rm-quiet, #8b93a7); padding: 6px 8px 2px;\n}\n.rm-composer-menu button[role="menuitem"] {\n text-align: left; border: none; background: transparent; color: inherit;\n font: inherit; font-size: 13px; padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-composer-menu button[role="menuitem"]:hover,\n.rm-composer-menu button[role="menuitem"].is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent);\n}\n.rm-composer-check {\n display: flex; align-items: center; gap: 8px; font-size: 13px; padding: 4px 8px; cursor: pointer;\n}\n.rm-grant-row {\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n padding: 2px 8px; font-size: 12.5px;\n}\n.rm-grant-row select {\n font: inherit; font-size: 12px; border-radius: 6px;\n border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; padding: 2px 6px;\n}\n.rm-peer-grant-block { border-top: 1px solid var(--rm-border, #2a2f3a); padding-top: 4px; margin-top: 4px; }\n.rm-peer-grant-name { font-size: 12px; font-weight: 600; padding: 4px 8px; overflow: hidden; text-overflow: ellipsis; }\n\n/* Hide legacy tight composer if still referenced */\n.rm-composer { display: none; }\n\n.rm-composer-grow { position: relative; }\n.rm-mention-menu {\n position: absolute; left: 8px; right: 8px; bottom: calc(100% + 6px); z-index: 50;\n max-height: 240px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 10px 28px rgba(0,0,0,.32);\n padding: 4px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-mention-item {\n display: flex; align-items: center; gap: 8px; text-align: left;\n border: none; background: transparent; color: inherit; font: inherit;\n padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-mention-item:hover, .rm-mention-item.is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 18%, transparent);\n}\n.rm-mention-item-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }\n.rm-mention-item-name { font-size: 13px; font-weight: 600; }\n.rm-mention-item-meta { font-size: 11px; color: var(--rm-quiet, #8b93a7); }\n\n\n/* New Room shortcut under workspace (mirrors New Session affordance) */\n.rm-new-room {\n display: flex; align-items: center; justify-content: center; gap: 6px;\n width: 100%; height: 36px; margin-top: 8px;\n border: .5px solid var(--rm-border); border-radius: 10px;\n background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n color: var(--rm-label); cursor: pointer; font-weight: 500; font-size: 13.5px;\n}\n.rm-new-room:hover { background: var(--dsw-alias-button-floating-hover, var(--rm-hover)); }\n.rm-new-room svg { flex: none; }\n\n/* Wide / landscape create + browse modals (overrides default 380px Modal card) */\n.rm-modal-wide {\n width: min(720px, calc(100vw - 48px)) !important;\n max-height: min(640px, calc(100vh - 48px));\n}\n.rm-form-wide {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px 18px;\n align-items: start;\n}\n.rm-form-wide .rm-span-2 { grid-column: 1 / -1; }\n.rm-browse { display: flex; flex-direction: column; gap: 12px; min-height: 280px; max-height: 420px; }\n.rm-browse-search { flex: none; }\n.rm-browse-status { font-size: 11.5px; color: var(--rm-quiet); white-space: nowrap; }\n.rm-browse-list { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 6px; }\n@media (max-width: 640px) {\n .rm-form-wide { grid-template-columns: 1fr; }\n .rm-modal-wide { width: min(420px, calc(100vw - 32px)) !important; }\n}\n';
1558
+ var rooms_default = '/* Collab Rooms UI \u2014 original surface inside the dsh shell. Uses only dsh --dsw-* tokens.\n Independent Rooms surface. Prefix: rm- (rooms). */\n\n.rm-root {\n --rm-accent: var(--dsw-alias-state-business-primary);\n --rm-hover: var(--dsw-alias-interactive-bg-hover);\n --rm-border: var(--dsw-alias-border-l2);\n --rm-bg: var(--dsw-alias-bg-base);\n --rm-panel: var(--dsw-alias-bg-module-platform);\n --rm-label: var(--dsw-alias-label-primary);\n --rm-muted: var(--dsw-alias-label-secondary);\n --rm-quiet: var(--dsw-alias-label-tertiary);\n font: 13px/1.5 var(--dsw-font-family);\n color: var(--rm-label);\n box-sizing: border-box;\n}\n.rm-root *, .rm-root *::before, .rm-root *::after { box-sizing: border-box; }\n.rm-root button, .rm-root input, .rm-root textarea { font: inherit; }\n\n/* \u2500\u2500 sidebar (joined rooms + public search) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-side {\n display: flex; flex-direction: column; height: 100%; min-height: 0;\n padding: 0 8px 8px 0; gap: 8px;\n}\n.rm-side-toolbar {\n display: flex; align-items: center; gap: 6px; flex: none; padding: 4px 0 2px;\n}\n.rm-side-title {\n flex: 1; font-size: 12px; font-weight: 600; letter-spacing: .04em;\n text-transform: uppercase; color: var(--rm-quiet);\n}\n.rm-icon-btn {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; padding: 0;\n}\n.rm-icon-btn:hover { background: var(--rm-hover); color: var(--rm-label); }\n.rm-search {\n display: flex; align-items: center; gap: 6px; height: 32px; padding: 0 10px;\n border: .5px solid var(--rm-border); border-radius: 8px; background: var(--rm-panel);\n}\n.rm-search input {\n flex: 1; min-width: 0; border: 0; background: transparent; outline: none; color: var(--rm-label); font-size: 13px;\n}\n.rm-search input::placeholder { color: var(--rm-quiet); }\n.rm-side-scroll { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 12px; }\n.rm-group-label { font-size: 11px; font-weight: 600; color: var(--rm-quiet); padding: 0 4px 4px; text-transform: uppercase; letter-spacing: .03em; }\n.rm-room-list { display: flex; flex-direction: column; gap: 2px; }\n.rm-room-item {\n display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px;\n border: 0; border-radius: 10px; background: transparent; color: inherit; cursor: pointer; text-align: left;\n}\n.rm-room-item:hover { background: var(--rm-hover); }\n.rm-room-item[aria-current="true"] { background: var(--rm-hover); }\n.rm-room-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }\n.rm-room-item-name { font-weight: 600; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-meta { font-size: 11.5px; color: var(--rm-quiet); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-trail { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }\n.rm-badge {\n display: inline-flex; align-items: center; gap: 3px; height: 18px; padding: 0 6px;\n border-radius: 999px; font-size: 10.5px; font-weight: 500; background: var(--rm-panel); color: var(--rm-muted);\n border: .5px solid var(--rm-border);\n}\n.rm-badge-public { color: var(--rm-accent); border-color: color-mix(in srgb, var(--rm-accent) 35%, transparent); background: color-mix(in srgb, var(--rm-accent) 8%, transparent); }\n.rm-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--rm-quiet); }\n.rm-dot-online { background: var(--dsw-alias-state-success-primary, #2bb673); }\n.rm-dot-connecting { background: var(--dsw-alias-state-warn-primary, #e9a23b); }\n.rm-dot-offline { background: var(--dsw-alias-state-error-primary, #e5484d); }\n.rm-empty-hint { padding: 8px 10px; font-size: 12px; color: var(--rm-quiet); line-height: 1.45; }\n.rm-dir-item {\n display: flex; align-items: flex-start; gap: 10px; padding: 10px; border-radius: 10px;\n border: .5px solid var(--rm-border); background: var(--rm-bg); margin-bottom: 6px;\n}\n.rm-dir-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }\n.rm-dir-tags { display: flex; flex-wrap: wrap; gap: 4px; }\n.rm-tag { font-size: 11px; color: var(--rm-muted); background: var(--rm-hover); border-radius: 4px; padding: 0 5px; }\n.rm-menu {\n position: fixed; z-index: 1000; min-width: 180px; padding: 4px;\n border-radius: 10px; background: var(--dsw-alias-bg-layer-1, var(--rm-bg));\n border: .5px solid var(--rm-border);\n box-shadow: 0 8px 24px color-mix(in srgb, var(--rm-label) 12%, transparent);\n display: flex; flex-direction: column;\n}\n.rm-menu button {\n display: flex; align-items: center; gap: 8px; height: 32px; padding: 0 10px;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-menu button:hover { background: var(--rm-hover); }\n\n/* \u2500\u2500 main: room workspace \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-main { display: flex; flex: 1; min-height: 0; height: 100%; width: 100%; background: var(--rm-bg); }\n.rm-workspace { display: flex; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat-header {\n flex: none; display: flex; align-items: center; gap: 10px; height: 48px;\n padding: 0 16px; border-bottom: .5px solid var(--rm-border);\n}\n.rm-chat-header-title { font-size: 14px; font-weight: 600; }\n.rm-chat-header-sub { font-size: 12px; color: var(--rm-quiet); }\n.rm-messages {\n flex: 1; min-height: 0; overflow: auto; padding: 16px 18px 8px;\n display: flex; flex-direction: column; gap: 2px;\n}\n.rm-day {\n align-self: center; margin: 10px 0 12px; padding: 2px 10px; border-radius: 999px;\n font-size: 11px; color: var(--rm-quiet); background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n}\n.rm-msg {\n display: grid; grid-template-columns: 32px 1fr; gap: 0 10px; padding: 4px 6px; border-radius: 8px;\n max-width: 720px;\n}\n.rm-msg:hover { background: color-mix(in srgb, var(--rm-hover) 60%, transparent); }\n.rm-msg-self { margin-left: auto; grid-template-columns: 1fr; max-width: 640px; }\n.rm-msg-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 1px; }\n.rm-msg-author { font-size: 12px; font-weight: 600; color: var(--rm-muted); }\n.rm-msg-time { font-size: 11px; color: var(--rm-quiet); }\n.rm-msg-body { font-size: 13.5px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; }\n.rm-msg-self .rm-msg-body {\n background: var(--dsw-alias-label-primary); color: var(--dsw-alias-label-primary-foreground);\n padding: 8px 12px; border-radius: 12px 12px 4px 12px; width: fit-content; margin-left: auto;\n}\n.rm-msg-event {\n grid-template-columns: 1fr; max-width: 560px; margin: 6px 0;\n padding: 8px 12px; border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-panel);\n}\n.rm-mention { color: var(--rm-accent); font-weight: 500; }\n.rm-msg-self .rm-mention { color: inherit; text-decoration: underline; text-underline-offset: 2px; }\n.rm-task-card { display: flex; flex-direction: column; gap: 4px; }\n.rm-task-head { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--rm-muted); }\n.rm-task-title { font-weight: 600; font-size: 13.5px; }\n.rm-composer {\n flex: none; display: flex; align-items: flex-end; gap: 8px; padding: 10px 16px 14px;\n border-top: .5px solid var(--rm-border);\n}\n.rm-composer-box {\n flex: 1; display: flex; align-items: flex-end; gap: 6px; min-height: 44px;\n padding: 8px 10px; border: .5px solid var(--rm-border); border-radius: 12px; background: var(--rm-panel);\n}\n.rm-composer-box:focus-within { border-color: color-mix(in srgb, var(--rm-accent) 50%, var(--rm-border)); }\n.rm-composer-box textarea {\n flex: 1; min-height: 28px; max-height: 120px; border: 0; background: transparent; outline: none;\n resize: none; color: var(--rm-label); line-height: 1.45;\n}\n.rm-composer-box textarea::placeholder { color: var(--rm-quiet); }\n.rm-composer-mode {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; flex: none;\n}\n.rm-composer-mode:hover, .rm-composer-mode[aria-pressed="true"] {\n color: var(--rm-accent); background: color-mix(in srgb, var(--rm-accent) 12%, transparent);\n}\n.rm-chat-hint { margin: 48px auto; text-align: center; color: var(--rm-quiet); font-size: 13px; max-width: 360px; line-height: 1.5; }\n\n/* \u2500\u2500 detail column (members / invite / about) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-detail {\n flex: 0 0 280px; width: 280px; display: flex; flex-direction: column; min-height: 0;\n border-left: .5px solid var(--rm-border); background: var(--rm-bg); overflow: auto;\n}\n.rm-detail-section { padding: 14px 14px 8px; border-bottom: .5px solid var(--rm-border); }\n.rm-detail-section:last-child { border-bottom: 0; }\n.rm-detail-h {\n display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600;\n text-transform: uppercase; letter-spacing: .04em; color: var(--rm-quiet); margin-bottom: 8px;\n}\n.rm-member {\n display: flex; align-items: center; gap: 8px; padding: 5px 4px; border-radius: 8px; min-height: 34px;\n}\n.rm-member:hover { background: var(--rm-hover); }\n.rm-member-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }\n.rm-member-actions { display: none; gap: 2px; }\n.rm-member:hover .rm-member-actions, .rm-member:focus-within .rm-member-actions { display: inline-flex; }\n.rm-mini {\n width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-muted); cursor: pointer; font-size: 12px;\n}\n.rm-mini:hover { background: var(--dsw-alias-interactive-bg-active); color: var(--rm-label); }\n.rm-task-row { display: flex; align-items: center; gap: 6px; padding: 4px; font-size: 12.5px; }\n.rm-task-row-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-about { display: grid; grid-template-columns: 88px 1fr; gap: 6px 8px; font-size: 12.5px; margin: 0; }\n.rm-about dt { color: var(--rm-quiet); margin: 0; }\n.rm-about dd { margin: 0; min-width: 0; overflow-wrap: anywhere; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }\n.rm-mono { font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace); font-size: 11.5px; }\n.rm-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }\n.rm-danger { color: var(--dsw-alias-state-error-primary, #e5484d) !important; }\n.rm-note { font-size: 12px; color: var(--rm-quiet); line-height: 1.45; margin: 0 0 8px; }\n\n/* \u2500\u2500 empty / welcome \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-welcome-wrap {\n flex: 1; display: flex; align-items: center; justify-content: center; padding: 32px; background: var(--rm-bg);\n}\n.rm-welcome {\n width: min(420px, 100%); display: flex; flex-direction: column; gap: 10px;\n padding: 20px; border-radius: 14px; border: .5px solid var(--rm-border); background: var(--rm-panel);\n}\n.rm-welcome h1 { margin: 0; font-size: 16px; font-weight: 600; }\n.rm-welcome p { margin: 0; font-size: 13px; color: var(--rm-muted); line-height: 1.5; }\n.rm-welcome-actions { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }\n.rm-welcome-action {\n display: flex; align-items: flex-start; gap: 10px; width: 100%; padding: 10px 12px;\n border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-bg);\n color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-welcome-action:hover { background: var(--rm-hover); }\n.rm-welcome-action b { display: block; font-size: 13px; }\n.rm-welcome-action span { display: block; font-size: 12px; color: var(--rm-quiet); margin-top: 2px; }\n\n/* \u2500\u2500 avatars \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-avatar {\n display: inline-flex; align-items: center; justify-content: center; flex: none;\n font-weight: 600; letter-spacing: .02em; user-select: none;\n}\n.rm-avatar-wrap { position: relative; display: inline-flex; flex: none; }\n\n/* \u2500\u2500 dialogs (inside dsh Modal) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-form { display: flex; flex-direction: column; gap: 14px; }\n.rm-field { display: flex; flex-direction: column; gap: 6px; }\n.rm-field-label { font-size: 13px; font-weight: 500; }\n.rm-field-hint { font-size: 12px; color: var(--rm-quiet); }\n.rm-name-row { display: flex; align-items: center; gap: 10px; }\n.rm-name-row > :last-child { flex: 1; }\n.rm-switch-row {\n display: flex; align-items: center; justify-content: space-between; gap: 14px;\n padding: 10px 12px; border-radius: 10px; background: var(--rm-panel);\n}\n.rm-error { color: var(--dsw-alias-state-error-primary, #e5484d); font-size: 12.5px; }\n.rm-invite {\n display: block; padding: 10px 12px; border-radius: 8px; background: var(--rm-panel);\n font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace);\n font-size: 12px; word-break: break-all; user-select: all;\n}\n\n/* Dual transport + approvals + agent config */\n.rm-mode-row { display: flex; gap: 8px; }\n.rm-mode-btn {\n flex: 1; padding: 10px 12px; border-radius: 10px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer; font: inherit;\n}\n.rm-mode-btn.is-on { border-color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent); }\n.rm-lan-browse { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; align-items: center; }\n.rm-lan-peer {\n font-size: 12px; padding: 4px 8px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer;\n}\n.rm-approvals { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); background: color-mix(in srgb, #f5a524 12%, transparent); }\n.rm-approval { display: flex; gap: 12px; align-items: center; justify-content: space-between; }\n.rm-approval-body { font-size: 13px; }\n.rm-approval-actions { display: flex; gap: 6px; flex-shrink: 0; }\n.rm-input { width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; font: inherit; }\n\n.rm-ws-bar { display: flex; flex-direction: column; gap: 4px; padding: 8px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); }\n.rm-ws-chip { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; cursor: pointer; font: inherit; font-size: 12.5px; }\n.rm-ws-path { font-size: 11px; color: var(--rm-quiet, #8b93a7); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item.is-active { background: color-mix(in srgb, var(--rm-accent, #5b8cff) 12%, transparent); }\n\n/* Native-aligned room composer (mirrors dsh conversation control row) */\n.rm-composer-card {\n box-sizing: border-box;\n width: 100%;\n max-width: 920px;\n margin: 0 auto;\n border-radius: 16px;\n background: var(--rm-elev, var(--rm-panel, #12151c));\n border: 1px solid var(--rm-border, #2a2f3a);\n box-shadow: 0 1px 2px rgba(0,0,0,.06);\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 8px 0 6px;\n}\n.rm-composer-grow { position: relative; padding: 0 4px; }\n.rm-composer-grow textarea {\n box-sizing: border-box; width: 100%; min-height: 36px; max-height: 160px;\n resize: none; border: none; outline: none; background: transparent;\n color: inherit; font: inherit; font-size: 14px; line-height: 22px;\n padding: 4px 12px 0 14px;\n}\n.rm-composer-grow textarea:disabled { opacity: .55; cursor: not-allowed; }\n.rm-composer-row {\n display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;\n gap: 10px; min-width: 0; padding: 2px 8px 4px;\n}\n.rm-composer-tools, .rm-composer-modes, .rm-composer-trailing {\n display: flex; align-items: center; min-width: 0; gap: 8px;\n}\n.rm-composer-trailing { flex: none; margin-left: auto; }\n.rm-composer-add {\n width: 28px; height: 28px; border: none; border-radius: 999px;\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n color: inherit; cursor: pointer; display: grid; place-items: center;\n}\n.rm-composer-add:hover, .rm-composer-add[aria-pressed="true"] {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 22%, transparent);\n}\n.rm-composer-select {\n max-width: 220px; height: 28px; border: none; border-radius: 8px;\n background: transparent; color: var(--rm-quiet, #8b93a7);\n font: inherit; font-size: 13px; font-weight: 500; line-height: 20px;\n padding: 0 8px; cursor: pointer; white-space: nowrap;\n}\n.rm-composer-select:hover:not(:disabled) {\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n}\n.rm-composer-select:disabled { opacity: .5; cursor: default; }\n.rm-composer-model-text { display: inline; }\n.rm-composer-model-icon { display: none; font-size: 10px; }\n.rm-composer-effort {\n margin-left: 6px; font-size: 11px; opacity: .75;\n text-transform: uppercase; letter-spacing: .02em;\n}\n.rm-composer-send {\n width: 34px; height: 34px; border: none; border-radius: 999px;\n background: var(--rm-accent, #5b8cff); color: #fff; cursor: pointer;\n display: grid; place-items: center; transform: translateY(-2px);\n}\n.rm-composer-send:hover:not(:disabled) { filter: brightness(1.08); }\n.rm-composer-send:disabled { opacity: .4; cursor: default; }\n.rm-composer-popwrap { position: relative; }\n.rm-composer-menu {\n position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 40;\n min-width: 200px; max-height: 320px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 8px 24px rgba(0,0,0,.28);\n padding: 6px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-composer-menu-wide { min-width: 280px; max-width: 340px; }\n.rm-composer-menu-label {\n font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;\n color: var(--rm-quiet, #8b93a7); padding: 6px 8px 2px;\n}\n.rm-composer-menu button[role="menuitem"] {\n text-align: left; border: none; background: transparent; color: inherit;\n font: inherit; font-size: 13px; padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-composer-menu button[role="menuitem"]:hover,\n.rm-composer-menu button[role="menuitem"].is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent);\n}\n.rm-composer-check {\n display: flex; align-items: center; gap: 8px; font-size: 13px; padding: 4px 8px; cursor: pointer;\n}\n.rm-grant-row {\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n padding: 2px 8px; font-size: 12.5px;\n}\n.rm-grant-row select {\n font: inherit; font-size: 12px; border-radius: 6px;\n border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; padding: 2px 6px;\n}\n.rm-peer-grant-block { border-top: 1px solid var(--rm-border, #2a2f3a); padding-top: 4px; margin-top: 4px; }\n.rm-peer-grant-name { font-size: 12px; font-weight: 600; padding: 4px 8px; overflow: hidden; text-overflow: ellipsis; }\n\n/* Hide legacy tight composer if still referenced */\n.rm-composer { display: none; }\n\n.rm-composer-grow { position: relative; }\n.rm-mention-menu {\n position: absolute; left: 8px; right: 8px; bottom: calc(100% + 6px); z-index: 50;\n max-height: 240px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 10px 28px rgba(0,0,0,.32);\n padding: 4px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-mention-item {\n display: flex; align-items: center; gap: 8px; text-align: left;\n border: none; background: transparent; color: inherit; font: inherit;\n padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-mention-item:hover, .rm-mention-item.is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 18%, transparent);\n}\n.rm-mention-item-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }\n.rm-mention-item-name { font-size: 13px; font-weight: 600; }\n.rm-mention-item-meta { font-size: 11px; color: var(--rm-quiet, #8b93a7); }\n\n\n/* New Room shortcut under workspace (mirrors New Session affordance) */\n.rm-new-room {\n display: flex; align-items: center; justify-content: center; gap: 6px;\n width: 100%; height: 36px; margin-top: 8px;\n border: .5px solid var(--rm-border); border-radius: 10px;\n background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n color: var(--rm-label); cursor: pointer; font-weight: 500; font-size: 13.5px;\n}\n.rm-new-room:hover { background: var(--dsw-alias-button-floating-hover, var(--rm-hover)); }\n.rm-new-room svg { flex: none; }\n\n/* Wide / landscape create + browse modals (overrides default 380px Modal card) */\n.rm-modal-wide {\n width: min(720px, calc(100vw - 48px)) !important;\n max-height: min(640px, calc(100vh - 48px));\n}\n.rm-form-wide {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px 18px;\n align-items: start;\n}\n.rm-form-wide .rm-span-2 { grid-column: 1 / -1; }\n.rm-browse { display: flex; flex-direction: column; gap: 12px; min-height: 280px; max-height: 420px; }\n.rm-browse-search { flex: none; }\n.rm-browse-status { font-size: 11.5px; color: var(--rm-quiet); white-space: nowrap; }\n.rm-browse-list { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 6px; }\n@media (max-width: 640px) {\n .rm-form-wide { grid-template-columns: 1fr; }\n .rm-modal-wide { width: min(420px, calc(100vw - 32px)) !important; }\n}\n\n\n/* \u2500\u2500 panel layout: rooms nav (grouped by native dsh workspace) + content \u2500\u2500 */\n.rm-main { flex-direction: row; }\n.rm-main-body { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; }\n.rm-main-body > .rm-welcome-wrap { flex: 1; }\n.rm-main-body > .rm-workspace { flex: 1; }\n.rm-nav {\n flex: 0 0 248px; width: 248px; display: flex; flex-direction: column; gap: 8px; min-height: 0;\n padding: 10px 8px; border-right: .5px solid var(--rm-border); background: var(--rm-bg);\n}\n.rm-nav-head { display: flex; align-items: center; gap: 4px; padding: 0 4px; }\n.rm-nav-scroll { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 10px; }\n.rm-ws-group { display: flex; flex-direction: column; gap: 2px; }\n.rm-ws-head {\n display: flex; align-items: center; gap: 6px; height: 30px; padding: 0 2px 0 8px;\n font-size: 12.5px; font-weight: 600; color: var(--rm-muted);\n}\n.rm-ws-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-ws-add { width: 24px; height: 24px; }\n.rm-ws-empty { padding: 2px 10px 6px 30px; font-size: 11.5px; }\n.rm-nav .rm-room-item { padding: 6px 8px 6px 14px; }\n.rm-session-mark { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; color: var(--rm-accent, var(--dsw-alias-state-business-primary)); }\n';
1532
1559
 
1533
1560
  // src/client/store.js
1534
1561
  var API = "/api/collab";
@@ -1644,13 +1671,21 @@ function createRoomsStore() {
1644
1671
  searchTimer = setTimeout(() => void runSearch(query), 300);
1645
1672
  },
1646
1673
  async create(form) {
1647
- const r = await post("/rooms", { ...form, workspaceId: form.workspaceId || snap.activeWorkspaceId });
1674
+ const r = await post("/rooms", { ...form, workspaceId: form.workspacePath ? void 0 : form.workspaceId || snap.activeWorkspaceId });
1675
+ try {
1676
+ api.onRoomOpened && api.onRoomOpened();
1677
+ } catch {
1678
+ }
1648
1679
  set({ selected: r.roomId, dialog: { kind: "invite", roomId: r.roomId, name: r.name, invite: r.invite, public: r.public, listed: r.listed } });
1649
1680
  void loadMessages(r.roomId);
1650
1681
  return r;
1651
1682
  },
1652
1683
  async join(invite, info) {
1653
- const r = await post("/join", { invite, workspaceId: snap.activeWorkspaceId, ...info || {} });
1684
+ const r = await post("/join", { invite, ...info && info.workspacePath ? {} : { workspaceId: snap.activeWorkspaceId }, ...info || {} });
1685
+ try {
1686
+ api.onRoomOpened && api.onRoomOpened();
1687
+ } catch {
1688
+ }
1654
1689
  set((s) => ({ selected: r.roomId, dialog: null, search: { ...s.search, results: s.search.results.map((x) => x.roomId === r.roomId ? { ...x, joined: true } : x) } }));
1655
1690
  void loadMessages(r.roomId);
1656
1691
  toast(`Joined ${r.name}`);
@@ -1700,7 +1735,7 @@ function createRoomsStore() {
1700
1735
  return post(`/rooms/${encodeURIComponent(roomId)}/mcp`, { tool, args, target });
1701
1736
  }
1702
1737
  };
1703
- return {
1738
+ const api = {
1704
1739
  getSnapshot: () => snap,
1705
1740
  subscribe(fn) {
1706
1741
  subs.add(fn);
@@ -1713,14 +1748,20 @@ function createRoomsStore() {
1713
1748
  es = null;
1714
1749
  subs.clear();
1715
1750
  clearTimeout(searchTimer);
1716
- }
1751
+ },
1752
+ /** Native dsh workspaces service (set by apply). */
1753
+ native: null,
1754
+ nativeList: null,
1755
+ /** Called after a room is created/joined (apply switches to the DSH Room panel). */
1756
+ onRoomOpened: null
1717
1757
  };
1758
+ return api;
1718
1759
  }
1719
1760
 
1720
1761
  // src/client/index.jsx
1721
1762
  var import_jsx_runtime = require("react/jsx-runtime");
1722
1763
  var name = "dsh-room-client";
1723
- var inject = ["slots", "layout"];
1764
+ var inject = ["slots", "layout", "workspaces"];
1724
1765
  var PANEL_ID = "collab";
1725
1766
  var Svg = ({ size = 16, children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: size, height: size, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.15", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children });
1726
1767
  var RoomsIcon = ({ size }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size, children: [
@@ -1733,7 +1774,6 @@ var SearchIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { child
1733
1774
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m10.2 10.2 3.3 3.3" })
1734
1775
  ] });
1735
1776
  var PlusIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Svg, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 3v10M3 8h10" }) });
1736
- var CloseIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Svg, { size: 14, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3.5 3.5l9 9M12.5 3.5l-9 9" }) });
1737
1777
  var SendIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Svg, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 13V3.5M4 7.5l4-4 4 4" }) });
1738
1778
  var LinkIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Svg, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6.5 9.5l3-3M7 4.75l1-1a2.5 2.5 0 0 1 3.5 3.5l-1 1M9 11.25l-1 1a2.5 2.5 0 0 1-3.5-3.5l1-1" }) });
1739
1779
  var TaskIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { children: [
@@ -1778,212 +1818,121 @@ var STATUS_TONE = { open: "info", claimed: "warning", done: "success", failed: "
1778
1818
  function useStore(store) {
1779
1819
  return (0, import_react.useSyncExternalStore)(store.subscribe, store.getSnapshot);
1780
1820
  }
1781
- function renderMentions(text) {
1782
- return text.split(/(@[A-Za-z0-9][\w.:-]*)/g).map((part, i) => part.startsWith("@") ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mention", children: part }, i) : part);
1783
- }
1784
- function PanelIcon({ size }) {
1785
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsIcon, { size });
1821
+ var EMPTY_WS = { items: [] };
1822
+ var NO_SUB = () => () => {
1823
+ };
1824
+ var NO_SNAP = () => EMPTY_WS;
1825
+ function useNativeWorkspaces(store) {
1826
+ const src = store.nativeList;
1827
+ const snap = (0, import_react.useSyncExternalStore)(src ? src.subscribe : NO_SUB, src ? src.getSnapshot : NO_SNAP);
1828
+ return snap && snap.items || [];
1829
+ }
1830
+ var samePath = (a, b) => !!a && !!b && a.replace(/[\\/]+$/, "") === b.replace(/[\\/]+$/, "");
1831
+ function groupRooms(rooms, workspaces) {
1832
+ const groups = workspaces.map((w) => ({ ws: w, rooms: [] }));
1833
+ const other = [];
1834
+ for (const r of rooms) {
1835
+ const path = r.memberConfig && r.memberConfig.workspacePath;
1836
+ const g = groups.find((x) => samePath(x.ws.path, path));
1837
+ if (g) g.rooms.push(r);
1838
+ else other.push(r);
1839
+ }
1840
+ return { groups, other };
1841
+ }
1842
+ var wsTitle = (w) => w.title || (w.path || "").split(/[\\/]/).filter(Boolean).pop() || w.path;
1843
+ function RoomItem({ r, selected, onClick }) {
1844
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-room-item", "aria-current": selected ? "true" : void 0, "data-room-id": r.roomId, onClick, children: [
1845
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-avatar-wrap", children: [
1846
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: r.name, size: 26 }),
1847
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-dot rm-dot-" + r.status, style: { position: "absolute", right: -1, bottom: -1, border: "2px solid var(--rm-bg)" } })
1848
+ ] }),
1849
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-room-item-body", children: [
1850
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-name", children: r.name }),
1851
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-meta", children: r.last ? `${r.last.from}: ${r.last.text}` : r.description || (r.public ? "Public room" : "Private \xB7 invite only") })
1852
+ ] }),
1853
+ r.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge rm-badge-public", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}) })
1854
+ ] });
1786
1855
  }
1787
- function RoomsSidebar({ store, ensurePanel }) {
1856
+ function RoomsNav({ store }) {
1788
1857
  const s = useStore(store);
1789
1858
  const a = store.actions;
1790
- const [menu, setMenu] = (0, import_react.useState)(null);
1791
- const [joining, setJoining] = (0, import_react.useState)(null);
1792
- const inputRef = (0, import_react.useRef)(null);
1793
- const searching = !!s.search.open;
1794
- const activeWs = (s.workspaces || []).find((w) => w.id === s.activeWorkspaceId) || (s.workspaces || [])[0] || null;
1795
- (0, import_react.useEffect)(() => {
1796
- if (!menu) return;
1797
- const close = () => setMenu(null);
1798
- window.addEventListener("click", close);
1799
- return () => window.removeEventListener("click", close);
1800
- }, [menu]);
1801
- (0, import_react.useEffect)(() => {
1802
- if (searching && inputRef.current) inputRef.current.focus();
1803
- }, [searching]);
1804
- const q = (s.search.query || "").trim().toLowerCase();
1805
- const mine = (s.rooms || []).filter((r) => {
1806
- if (activeWs && r.workspaceId && r.workspaceId !== activeWs.id) return false;
1807
- if (!q) return true;
1808
- return r.name.toLowerCase().includes(q) || (r.description || "").toLowerCase().includes(q) || (r.tags || []).some((t) => ("#" + t).includes(q));
1809
- });
1810
- const join = async (r) => {
1811
- setJoining(r.roomId);
1812
- try {
1813
- await a.join(r.invite, { name: r.name, description: r.description, tags: r.tags, public: true, workspaceId: activeWs?.id });
1814
- ensurePanel();
1815
- } catch (e) {
1816
- a.toast(e.message);
1817
- } finally {
1818
- setJoining(null);
1819
- }
1820
- };
1821
- if (!activeWs) {
1822
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-side", "data-collab-sidebar": "", children: [
1823
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-side-toolbar", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-side-title", children: "Workspaces" }) }),
1824
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-side-scroll", children: [
1825
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-empty-hint", children: "Bind a local workspace first. Rooms you create inherit its path (MCP sandbox + agent cwd)." }),
1826
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "primary", onClick: () => a.openDialog({ kind: "workspace" }), children: "Add workspace" })
1827
- ] })
1828
- ] });
1829
- }
1830
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-side", "data-collab-sidebar": "", children: [
1831
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-side-toolbar", children: [
1832
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-side-title", children: "DSH Room" }),
1833
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-icon-btn", "aria-label": searching ? "Close search" : "Search rooms", "aria-pressed": searching, onClick: () => a.setSearchOpen(!searching), children: searching ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CloseIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}) }),
1834
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1835
- "button",
1836
- {
1837
- type: "button",
1838
- className: "rm-icon-btn",
1839
- "data-rm-new": "",
1840
- "aria-label": "New",
1841
- "aria-haspopup": "menu",
1842
- "aria-expanded": !!menu,
1843
- onClick: (e) => {
1844
- e.stopPropagation();
1845
- const r = e.currentTarget.getBoundingClientRect();
1846
- setMenu(menu ? null : { top: r.bottom + 4, left: Math.max(8, r.right - 200) });
1847
- },
1848
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {})
1849
- }
1850
- ),
1851
- menu ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-menu", role: "menu", style: { top: menu.top, left: menu.left }, onClick: (e) => e.stopPropagation(), children: [
1852
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
1853
- setMenu(null);
1854
- a.openDialog({ kind: "create" });
1855
- }, children: [
1856
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}),
1857
- "Create room in workspace"
1858
- ] }),
1859
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
1860
- setMenu(null);
1861
- a.openDialog({ kind: "join" });
1862
- }, children: [
1863
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkIcon, {}),
1864
- "Join by invite"
1865
- ] }),
1866
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
1867
- setMenu(null);
1868
- a.openDialog({ kind: "workspace" });
1869
- }, children: [
1870
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
1871
- "Add / switch workspace"
1872
- ] }),
1873
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
1874
- setMenu(null);
1875
- a.openDialog({ kind: "browse" });
1876
- }, children: [
1877
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
1878
- "Browse public rooms"
1879
- ] })
1880
- ] }) : null
1859
+ const workspaces = useNativeWorkspaces(store);
1860
+ const [q, setQ] = (0, import_react.useState)("");
1861
+ const qq = q.trim().toLowerCase();
1862
+ const match = (r) => !qq || r.name.toLowerCase().includes(qq) || (r.description || "").toLowerCase().includes(qq) || (r.tags || []).some((t) => ("#" + t).includes(qq));
1863
+ const { groups, other } = groupRooms((s.rooms || []).filter(match), workspaces);
1864
+ const sel = (r) => a.select(r.roomId);
1865
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("nav", { className: "rm-nav", "aria-label": "Rooms by workspace", "data-rm-nav": "", children: [
1866
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-nav-head", children: [
1867
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-side-title", children: "Rooms" }),
1868
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-icon-btn", title: "Browse public rooms", "aria-label": "Browse public rooms", onClick: () => a.openDialog({ kind: "browse" }), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}) }),
1869
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-icon-btn", title: "Join by invite", "aria-label": "Join by invite", onClick: () => a.openDialog({ kind: "join" }), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkIcon, {}) })
1881
1870
  ] }),
1882
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-bar", children: [
1883
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-ws-chip", onClick: () => a.openDialog({ kind: "workspace" }), title: activeWs.path, children: [
1884
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
1885
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: activeWs.name })
1886
- ] }),
1887
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-path", title: activeWs.path, children: activeWs.path }),
1888
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-new-room", "data-rm-new-room": "", onClick: () => {
1889
- a.openDialog({ kind: "create" });
1890
- ensurePanel();
1891
- }, children: [
1892
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}),
1893
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "New Room" })
1894
- ] })
1895
- ] }),
1896
- searching ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search", children: [
1871
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search", children: [
1897
1872
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
1898
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { ref: inputRef, type: "text", placeholder: "Search rooms or #tags", value: s.search.query, onChange: (e) => a.setQuery(e.target.value), onKeyDown: (e) => e.key === "Escape" && a.setSearchOpen(false), "aria-label": "Search rooms" })
1899
- ] }) : null,
1900
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-side-scroll", children: [
1901
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1902
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-group-label", children: [
1903
- "Rooms in ",
1904
- activeWs.name
1905
- ] }),
1906
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-room-list", children: [
1907
- mine.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1873
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "text", placeholder: "Filter rooms or #tags", value: q, onChange: (e) => setQ(e.target.value), "aria-label": "Filter rooms" })
1874
+ ] }),
1875
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-nav-scroll", children: [
1876
+ groups.map(({ ws, rooms }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-group", "data-rm-ws-group": ws.workspaceId, children: [
1877
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-head", title: ws.path, children: [
1878
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
1879
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-name", children: wsTitle(ws) }),
1880
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1908
1881
  "button",
1909
1882
  {
1910
1883
  type: "button",
1911
- className: "rm-room-item",
1912
- "aria-current": r.roomId === s.selected ? "true" : void 0,
1913
- "data-room-id": r.roomId,
1914
- onClick: () => {
1915
- a.select(r.roomId);
1916
- ensurePanel();
1917
- },
1918
- children: [
1919
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-avatar-wrap", children: [
1920
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: r.name, size: 30 }),
1921
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-dot rm-dot-" + r.status, style: { position: "absolute", right: -1, bottom: -1, border: "2px solid var(--rm-bg)" } })
1922
- ] }),
1923
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-room-item-body", children: [
1924
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-name", children: r.name }),
1925
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-meta", children: r.last ? `${r.last.from}: ${r.last.text}` : r.description || (r.public ? "Public room" : "Private \xB7 invite only") })
1926
- ] }),
1927
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-room-item-trail", children: [
1928
- r.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge rm-badge-public", children: [
1929
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
1930
- "Public"
1931
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge", children: [
1932
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}),
1933
- "Private"
1934
- ] }),
1935
- r.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
1936
- r.last ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 10.5, color: "var(--rm-quiet)" }, children: fmtTime(r.last.ts) }) : null
1937
- ] })
1938
- ]
1939
- },
1940
- r.roomId
1941
- )),
1942
- !mine.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-empty-hint", children: "No rooms in this workspace yet. Create one or join by invite \u2014 peers should check out the same project." }) : null
1943
- ] })
1944
- ] }),
1945
- (s.workspaces || []).length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1946
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-group-label", children: "Workspaces" }),
1947
- (s.workspaces || []).map((w) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-room-item" + (w.id === activeWs.id ? " is-active" : ""), onClick: () => a.setActiveWorkspace(w.id), children: [
1884
+ className: "rm-icon-btn rm-ws-add",
1885
+ title: `New room in ${wsTitle(ws)}`,
1886
+ "aria-label": `New room in ${wsTitle(ws)}`,
1887
+ onClick: () => a.openDialog({ kind: "create", workspace: { id: ws.workspaceId, path: ws.path, title: wsTitle(ws) } }),
1888
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {})
1889
+ }
1890
+ )
1891
+ ] }),
1892
+ rooms.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomItem, { r, selected: r.roomId === s.selected, onClick: () => sel(r) }, r.roomId)),
1893
+ !rooms.length && !qq ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-empty-hint rm-ws-empty", children: "No rooms \xB7 use + to create one here" }) : null
1894
+ ] }, ws.workspaceId)),
1895
+ other.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-group", children: [
1896
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-head", children: [
1948
1897
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
1949
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-room-item-body", children: [
1950
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-name", children: w.name }),
1951
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-meta", children: w.path })
1952
- ] })
1953
- ] }, w.id))
1954
- ] }) : null,
1955
- searching ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1956
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-group-label", children: [
1957
- "Public directory ",
1958
- s.search.loading ? "\xB7 searching\u2026" : ""
1898
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-name", children: "Other rooms" })
1959
1899
  ] }),
1960
- s.search.results.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-dir-item", "data-dir-room-id": r.roomId, children: [
1961
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: r.name, size: 30 }),
1962
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-dir-item-body", children: [
1963
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-room-item-name", children: r.name }),
1964
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-room-item-meta", children: [
1965
- r.description || "No description",
1966
- " \xB7 ",
1967
- r.members,
1968
- " online"
1969
- ] }),
1970
- (r.tags || []).length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-dir-tags", children: (r.tags || []).map((tg) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-tag", children: [
1971
- "#",
1972
- tg
1973
- ] }, tg)) }) : null
1974
- ] }),
1975
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: r.joined ? "ghost" : "outline", disabled: r.joined || joining === r.roomId, onClick: () => join(r), children: r.joined ? "Joined" : joining === r.roomId ? "\u2026" : "Join" })
1976
- ] }, r.roomId)),
1977
- !s.search.loading && !s.search.results.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-empty-hint", children: s.search.error ? `Directory error: ${s.search.error}` : "No public rooms match." }) : null,
1978
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-empty-hint", children: [
1979
- "Joining attaches the room to workspace \u201C",
1980
- activeWs.name,
1981
- "\u201D. Point your local path at the same project."
1982
- ] })
1900
+ other.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomItem, { r, selected: r.roomId === s.selected, onClick: () => sel(r) }, r.roomId))
1901
+ ] }) : null,
1902
+ !workspaces.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-empty-hint", children: [
1903
+ "No workspaces yet. Add one with the native \u201CAdd workspace\u201D in the left sidebar, then use \u201CNew room\u201D on it.",
1904
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginTop: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: () => a.openDialog({ kind: "workspace" }), children: "Add workspace\u2026" }) })
1983
1905
  ] }) : null
1984
1906
  ] })
1985
1907
  ] });
1986
1908
  }
1909
+ function RoomSessionMark({ store, sessionId }) {
1910
+ const s = useStore(store);
1911
+ const room = (s.rooms || []).find((r) => r.sessionId === sessionId);
1912
+ if (!room) return null;
1913
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-session-mark", title: `DSH Room \xB7 ${room.name}`, "aria-label": `DSH Room ${room.name}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsIcon, { size: 13 }) });
1914
+ }
1915
+ function WorkspaceSelect({ store, value, onChange, id }) {
1916
+ const workspaces = useNativeWorkspaces(store);
1917
+ (0, import_react.useEffect)(() => {
1918
+ if (!value && workspaces[0]) onChange({ id: workspaces[0].workspaceId, path: workspaces[0].path, title: wsTitle(workspaces[0]) });
1919
+ }, [workspaces.length]);
1920
+ if (!workspaces.length) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "No dsh workspace yet \u2014 add one in the left sidebar (Add workspace)." });
1921
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { id, className: "rm-input", value: value ? value.path : "", onChange: (e) => {
1922
+ const w = workspaces.find((x) => x.path === e.target.value);
1923
+ if (w) onChange({ id: w.workspaceId, path: w.path, title: wsTitle(w) });
1924
+ }, children: workspaces.map((w) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", { value: w.path, children: [
1925
+ wsTitle(w),
1926
+ " \u2014 ",
1927
+ w.path
1928
+ ] }, w.workspaceId)) });
1929
+ }
1930
+ function renderMentions(text) {
1931
+ return text.split(/(@[A-Za-z0-9][\w.:-]*)/g).map((part, i) => part.startsWith("@") ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mention", children: part }, i) : part);
1932
+ }
1933
+ function PanelIcon({ size }) {
1934
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsIcon, { size });
1935
+ }
1987
1936
  function Message({ m, showAuthor }) {
1988
1937
  if (m.kind === "task.create") {
1989
1938
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-msg rm-msg-event", "data-message-id": m.id, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-task-card", children: [
@@ -2419,32 +2368,26 @@ function RoomDetail({ room, messages, store, mention, assign }) {
2419
2368
  ] });
2420
2369
  }
2421
2370
  function EmptyMain({ store }) {
2422
- const s = useStore(store);
2423
2371
  const a = store.actions;
2424
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-root rm-welcome-wrap", "data-collab-main": "", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-welcome", children: [
2372
+ const workspaces = useNativeWorkspaces(store);
2373
+ const first = workspaces[0];
2374
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-welcome-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-welcome", children: [
2425
2375
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-badge", children: "Collab \xB7 E2EE \xB7 no accounts" }),
2426
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: "Workspaces, then rooms" }),
2427
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "Bind a local workspace (project folder), then create rooms inside it. Peers join those rooms and point their agent at the same project. @mention or tasks wake agents. Relay or LAN transport; E2EE; optional public directory." }),
2376
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: "Rooms live in your workspaces" }),
2377
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "Your dsh workspaces stay in the left sidebar as usual. Hover a workspace there and click the room icon (next to \u201CNew session\u201D), or use its \u201C\u2026\u201D menu \u2192 New room. Peers join and point their agent at the same project; @mention or tasks wake agents." }),
2428
2378
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-welcome-actions", children: [
2429
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "workspace" }), children: [
2430
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
2431
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
2432
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Bind workspace" }),
2433
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Local project path shared by agents in rooms" })
2434
- ] })
2435
- ] }),
2436
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "create" }), children: [
2379
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", disabled: !first, onClick: () => first && a.openDialog({ kind: "create", workspace: { id: first.workspaceId, path: first.path, title: wsTitle(first) } }), children: [
2437
2380
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}),
2438
2381
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
2439
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Create room" }),
2440
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "In the active workspace \xB7 Relay or LAN" })
2382
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "New room" }),
2383
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: first ? `In ${wsTitle(first)} \xB7 default relay 62.234.113.56:80` : "Add a workspace in the left sidebar first" })
2441
2384
  ] })
2442
2385
  ] }),
2443
2386
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "join" }), children: [
2444
2387
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkIcon, {}),
2445
2388
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
2446
2389
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Join by invite" }),
2447
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Paste a collab://join/\u2026 invite" })
2390
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Paste a collab://\u2026 invite" })
2448
2391
  ] })
2449
2392
  ] }),
2450
2393
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "browse" }), children: [
@@ -2461,51 +2404,60 @@ function RoomsMain({ store }) {
2461
2404
  const s = useStore(store);
2462
2405
  const [drafts, setDrafts] = (0, import_react.useState)({});
2463
2406
  const room = s.rooms.find((r) => r.roomId === s.selected);
2464
- if (!room) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyMain, { store });
2407
+ if (!room) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-main", "data-collab-main": "", children: [
2408
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsNav, { store }),
2409
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-main-body", children: [
2410
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApprovalBanner, { store }),
2411
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyMain, { store })
2412
+ ] })
2413
+ ] });
2465
2414
  const messages = s.messages[room.roomId] || [];
2466
2415
  const draft = drafts[room.roomId] || "";
2467
2416
  const setDraft = (v) => setDrafts((d) => ({ ...d, [room.roomId]: typeof v === "function" ? v(d[room.roomId] || "") : v }));
2468
2417
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-main", "data-collab-main": "", children: [
2469
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApprovalBanner, { store }),
2470
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-workspace", children: [
2471
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "rm-chat", children: [
2472
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "rm-chat-header", children: [
2473
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: room.name, size: 28 }),
2474
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
2475
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-chat-header-title", children: room.name }),
2476
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-chat-header-sub", children: [
2477
- room.members.filter((m) => m.online !== false).length,
2478
- " online \xB7 ",
2479
- room.public ? "public" : "private",
2480
- room.status !== "online" ? ` \xB7 room ${room.status}` : ""
2481
- ] })
2482
- ] }),
2483
- room.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge rm-badge-public", children: [
2484
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
2485
- "Public"
2486
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge", children: [
2487
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}),
2488
- "Private"
2418
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsNav, { store }),
2419
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-main-body", children: [
2420
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApprovalBanner, { store }),
2421
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-workspace", children: [
2422
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "rm-chat", children: [
2423
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "rm-chat-header", children: [
2424
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: room.name, size: 28 }),
2425
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
2426
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-chat-header-title", children: room.name }),
2427
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-chat-header-sub", children: [
2428
+ room.members.filter((m) => m.online !== false).length,
2429
+ " online \xB7 ",
2430
+ room.public ? "public" : "private",
2431
+ room.status !== "online" ? ` \xB7 room ${room.status}` : ""
2432
+ ] })
2433
+ ] }),
2434
+ room.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge rm-badge-public", children: [
2435
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
2436
+ "Public"
2437
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge", children: [
2438
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}),
2439
+ "Private"
2440
+ ] }),
2441
+ room.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
2442
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: room.status === "online" ? "done" : room.status === "connecting" ? "ongoing" : "error", size: 8 })
2489
2443
  ] }),
2490
- room.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
2491
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: room.status === "online" ? "done" : room.status === "connecting" ? "ongoing" : "error", size: 8 })
2444
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages }),
2445
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Composer, { room, store, draft, setDraft })
2492
2446
  ] }),
2493
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages }),
2494
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Composer, { room, store, draft, setDraft })
2495
- ] }),
2496
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2497
- RoomDetail,
2498
- {
2499
- room,
2500
- messages,
2501
- store,
2502
- mention: (id) => setDraft((d) => `${d}${d && !d.endsWith(" ") ? " " : ""}@${id} `),
2503
- assign: (id) => {
2504
- setDraft(`@${id} `);
2505
- store.actions.toast(`Switch to task mode, then describe the task for @${id}`);
2447
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2448
+ RoomDetail,
2449
+ {
2450
+ room,
2451
+ messages,
2452
+ store,
2453
+ mention: (id) => setDraft((d) => `${d}${d && !d.endsWith(" ") ? " " : ""}@${id} `),
2454
+ assign: (id) => {
2455
+ setDraft(`@${id} `);
2456
+ store.actions.toast(`Switch to task mode, then describe the task for @${id}`);
2457
+ }
2506
2458
  }
2507
- }
2508
- )
2459
+ )
2460
+ ] })
2509
2461
  ] })
2510
2462
  ] });
2511
2463
  }
@@ -2516,7 +2468,7 @@ function Field({ label, hint, children, htmlFor }) {
2516
2468
  hint ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: hint }) : null
2517
2469
  ] });
2518
2470
  }
2519
- function CreateDialog({ store }) {
2471
+ function CreateDialog({ store, d }) {
2520
2472
  const s = useStore(store);
2521
2473
  const a = store.actions;
2522
2474
  const [f, setF] = (0, import_react.useState)({ name: "", description: "", tags: "", public: false, mode: "lan", lanHostPort: "62.234.113.56:80" });
@@ -2524,9 +2476,13 @@ function CreateDialog({ store }) {
2524
2476
  const [busy, setBusy] = (0, import_react.useState)(false);
2525
2477
  const [lanPeers, setLanPeers] = (0, import_react.useState)([]);
2526
2478
  const ok = f.name.trim() && !busy;
2527
- const ws = (s.workspaces || []).find((w) => w.id === s.activeWorkspaceId) || (s.workspaces || [])[0];
2479
+ const [ws, setWs] = (0, import_react.useState)(d && d.workspace ? d.workspace : null);
2528
2480
  const submit = async () => {
2529
2481
  if (!ok) return;
2482
+ if (!ws) {
2483
+ setErr("Pick a workspace");
2484
+ return;
2485
+ }
2530
2486
  setBusy(true);
2531
2487
  setErr(null);
2532
2488
  try {
@@ -2536,7 +2492,9 @@ function CreateDialog({ store }) {
2536
2492
  tags: f.tags,
2537
2493
  public: f.public,
2538
2494
  mode: f.mode,
2539
- lanHostPort: f.mode === "lan" ? f.lanHostPort.trim() : void 0
2495
+ lanHostPort: f.mode === "lan" ? f.lanHostPort.trim() : void 0,
2496
+ workspacePath: ws.path,
2497
+ workspaceName: ws.title
2540
2498
  });
2541
2499
  } catch (e) {
2542
2500
  setErr(e.message);
@@ -2559,12 +2517,13 @@ function CreateDialog({ store }) {
2559
2517
  title: "Create room",
2560
2518
  closeLabel: "Close",
2561
2519
  className: "rm-modal-wide",
2562
- description: ws ? `In workspace \u201C${ws.name}\u201D. Default relay 62.234.113.56:80 \xB7 E2EE \xB7 keys stay local.` : "Bind a workspace first. Default relay 62.234.113.56:80.",
2520
+ description: ws ? `In workspace \u201C${ws.title}\u201D (${ws.path}). Agent cwd + MCP sandbox = this path. Default relay 62.234.113.56:80 \xB7 E2EE.` : "Pick a dsh workspace. Default relay 62.234.113.56:80 \xB7 E2EE.",
2563
2521
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2564
2522
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
2565
2523
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: !ok, onClick: submit, children: busy ? "Creating\u2026" : "Create" })
2566
2524
  ] }),
2567
2525
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form rm-form-wide", onKeyDown: (e) => e.key === "Enter" && e.target.tagName === "INPUT" && submit(), children: [
2526
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Workspace", htmlFor: "rm-create-ws", hint: "Native dsh workspace this room belongs to.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceSelect, { store, id: "rm-create-ws", value: ws, onChange: setWs }) }),
2568
2527
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Name", htmlFor: "rm-create-name", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-name-row", children: [
2569
2528
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: f.name || "room", size: 36 }),
2570
2529
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-name", autoFocus: true, placeholder: "e.g. release-crew", maxLength: 60, value: f.name, onChange: (e) => setF({ ...f, name: e.target.value }) })
@@ -2604,12 +2563,13 @@ function JoinDialog({ store }) {
2604
2563
  const [err, setErr] = (0, import_react.useState)(null);
2605
2564
  const [busy, setBusy] = (0, import_react.useState)(false);
2606
2565
  const valid = /^collab:\/\/(join|lan|relay)\/.+#\S+$/.test(invite.trim());
2566
+ const [ws, setWs] = (0, import_react.useState)(null);
2607
2567
  const submit = async () => {
2608
2568
  if (!valid || busy) return;
2609
2569
  setBusy(true);
2610
2570
  setErr(null);
2611
2571
  try {
2612
- await a.join(invite.trim());
2572
+ await a.join(invite.trim(), ws ? { workspacePath: ws.path, workspaceName: ws.title } : void 0);
2613
2573
  } catch (e) {
2614
2574
  setErr(e.message);
2615
2575
  setBusy(false);
@@ -2622,12 +2582,13 @@ function JoinDialog({ store }) {
2622
2582
  onClose: a.closeDialog,
2623
2583
  title: "Join room",
2624
2584
  closeLabel: "Close",
2625
- description: "Paste an invite. The room attaches to your active workspace \u2014 set the local path to your checkout of the same project before collaborating via MCP.",
2585
+ description: "Paste an invite and pick the dsh workspace (your checkout of the same project) the room attaches to.",
2626
2586
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2627
2587
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
2628
2588
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: !valid || busy, onClick: submit, children: busy ? "Joining\u2026" : "Join" })
2629
2589
  ] }),
2630
2590
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form", children: [
2591
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceSelect, { store, value: ws, onChange: setWs }),
2631
2592
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { autoFocus: true, "aria-label": "Invite", placeholder: "collab://join/\u2026 or collab://lan/<host>:<port>/<roomId>#\u2026", value: invite, onChange: (e) => setInvite(e.target.value), onKeyDown: (e) => e.key === "Enter" && submit() }),
2632
2593
  invite && !valid ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Invites: collab://join/\u2026#key, collab://lan/host:port/room#key, or collab://relay/host/join/room#key" }) : null,
2633
2594
  err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: err }) : null
@@ -2817,9 +2778,10 @@ function WorkspaceDialog({ store }) {
2817
2778
  setBusy(true);
2818
2779
  setErr(null);
2819
2780
  try {
2820
- await a.ensureWorkspace({ name: name2.trim() || void 0, path: path.trim() });
2781
+ if (store.native && store.native.create) await store.native.create({ path: path.trim() });
2782
+ else await a.ensureWorkspace({ name: name2.trim() || void 0, path: path.trim() });
2821
2783
  a.closeDialog();
2822
- a.toast("Workspace ready \u2014 create a room to collaborate");
2784
+ a.toast("Workspace added \u2014 it appears in the left sidebar; use New room on it");
2823
2785
  } catch (e) {
2824
2786
  setErr(e.message);
2825
2787
  setBusy(false);
@@ -2830,18 +2792,14 @@ function WorkspaceDialog({ store }) {
2830
2792
  {
2831
2793
  open: true,
2832
2794
  onClose: a.closeDialog,
2833
- title: "Workspace",
2795
+ title: "Add workspace",
2834
2796
  closeLabel: "Close",
2835
- description: "Pick the local project folder agents share. Rooms created next inherit this path (MCP + agent cwd). Peers map the same logical project to their own checkout.",
2797
+ description: "Fallback: registers a dsh workspace (same as the native \u201CAdd workspace\u201D). It shows up in the left sidebar.",
2836
2798
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2837
2799
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
2838
2800
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: busy || !path.trim(), onClick: submit, children: busy ? "Saving\u2026" : "Use workspace" })
2839
2801
  ] }),
2840
2802
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form", children: [
2841
- (s.workspaces || []).length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Switch to existing", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-lan-browse", children: (s.workspaces || []).map((w) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-lan-peer", onClick: async () => {
2842
- await a.setActiveWorkspace(w.id);
2843
- a.closeDialog();
2844
- }, children: w.name }, w.id)) }) }) : null,
2845
2803
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Name", htmlFor: "rm-ws-name", hint: "Shown in the sidebar.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-ws-name", placeholder: "e.g. collab", value: name2, onChange: (e) => setName(e.target.value) }) }),
2846
2804
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Local path", htmlFor: "rm-ws-path", hint: "Absolute path on this machine.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-ws-path", autoFocus: true, placeholder: "/Users/you/code/my-app", value: path, onChange: (e) => setPath(e.target.value), onKeyDown: (e) => e.key === "Enter" && submit() }) }),
2847
2805
  err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: err }) : null
@@ -2854,7 +2812,7 @@ function BrowseDialog({ store }) {
2854
2812
  const a = store.actions;
2855
2813
  const [joining, setJoining] = (0, import_react.useState)(null);
2856
2814
  const inputRef = (0, import_react.useRef)(null);
2857
- const activeWs = (s.workspaces || []).find((w) => w.id === s.activeWorkspaceId) || (s.workspaces || [])[0];
2815
+ const [activeWs, setActiveWs] = (0, import_react.useState)(null);
2858
2816
  (0, import_react.useEffect)(() => {
2859
2817
  a.setSearchOpen(true);
2860
2818
  requestAnimationFrame(() => inputRef.current?.focus());
@@ -2864,7 +2822,7 @@ function BrowseDialog({ store }) {
2864
2822
  const join = async (r) => {
2865
2823
  setJoining(r.roomId);
2866
2824
  try {
2867
- await a.join(r.invite, { name: r.name, description: r.description, tags: r.tags, public: true, workspaceId: activeWs?.id });
2825
+ await a.join(r.invite, { name: r.name, description: r.description, tags: r.tags, public: true, workspacePath: activeWs?.path, workspaceName: activeWs?.title });
2868
2826
  } catch (e) {
2869
2827
  a.toast(e.message);
2870
2828
  } finally {
@@ -2879,9 +2837,10 @@ function BrowseDialog({ store }) {
2879
2837
  title: "Browse public rooms",
2880
2838
  closeLabel: "Close",
2881
2839
  className: "rm-modal-wide",
2882
- description: "Directory on VPS 62.234.113.56:80. Search by name or #tag, then join into the active workspace.",
2840
+ description: "Directory on VPS 62.234.113.56:80. Search by name or #tag; joined rooms attach to the workspace below.",
2883
2841
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Close" }),
2884
2842
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-browse", children: [
2843
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceSelect, { store, value: activeWs, onChange: setActiveWs }),
2885
2844
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search rm-browse-search", children: [
2886
2845
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
2887
2846
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -2925,7 +2884,7 @@ function Overlay({ store }) {
2925
2884
  const s = useStore(store);
2926
2885
  const d = s.dialog;
2927
2886
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2928
- d?.kind === "create" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CreateDialog, { store }) : null,
2887
+ d?.kind === "create" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CreateDialog, { store, d }) : null,
2929
2888
  d?.kind === "join" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JoinDialog, { store }) : null,
2930
2889
  d?.kind === "invite" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InviteDialog, { store, d }) : null,
2931
2890
  d?.kind === "leave" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LeaveDialog, { store, d }) : null,
@@ -2944,8 +2903,118 @@ function installStyles() {
2944
2903
  document.head.appendChild(style);
2945
2904
  return () => style.remove();
2946
2905
  }
2906
+ var ROOM_SVG = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.15" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2.5" y="3" width="11" height="10" rx="2"/><path d="M5.5 6.5h5M5.5 9.5h3.5"/></svg>';
2907
+ function installWorkspaceDecorator(store, native, onNewRoom) {
2908
+ if (typeof document === "undefined" || !native) return () => {
2909
+ };
2910
+ const wsFor = (key) => (native.list.getSnapshot().items || []).find((w) => w.workspaceId === key);
2911
+ const rowKey = (row) => (row.getAttribute("data-row-key") || "").slice("workspace:".length);
2912
+ const open = (key) => {
2913
+ const w = wsFor(key);
2914
+ if (w) onNewRoom({ id: w.workspaceId, path: w.path, title: wsTitle(w) });
2915
+ };
2916
+ const decorate = () => {
2917
+ for (const row of document.querySelectorAll('[data-row-key^="workspace:"]')) {
2918
+ const key = rowKey(row);
2919
+ if (!key || row.querySelector("[data-rm-ws-new-room]")) continue;
2920
+ const newSess = row.querySelector('button[aria-label^="New session in"]');
2921
+ if (!newSess) continue;
2922
+ let anchor = newSess;
2923
+ while (anchor.parentElement && anchor.parentElement !== row && !anchor.parentElement.querySelector('button[aria-label^="Workspace actions"]')) anchor = anchor.parentElement;
2924
+ if (!anchor.parentElement) continue;
2925
+ const label = (newSess.getAttribute("aria-label") || "").replace(/^New session in /, "");
2926
+ const btn = document.createElement("button");
2927
+ btn.type = "button";
2928
+ btn.className = newSess.className;
2929
+ btn.setAttribute("data-rm-ws-new-room", key);
2930
+ btn.setAttribute("aria-label", `New room in ${label}`);
2931
+ btn.title = "New room";
2932
+ btn.innerHTML = ROOM_SVG;
2933
+ btn.addEventListener("click", (e) => {
2934
+ e.preventDefault();
2935
+ e.stopPropagation();
2936
+ open(key);
2937
+ });
2938
+ anchor.parentElement.insertBefore(btn, anchor);
2939
+ }
2940
+ };
2941
+ let pendingKey = null;
2942
+ const onClickCapture = (e) => {
2943
+ const trigger = e.target instanceof Element ? e.target.closest('button[aria-label^="Workspace actions for"]') : null;
2944
+ if (!trigger) return;
2945
+ const row = trigger.closest('[data-row-key^="workspace:"]');
2946
+ pendingKey = row ? rowKey(row) : null;
2947
+ if (!pendingKey) return;
2948
+ const key = pendingKey;
2949
+ let tries = 0;
2950
+ const augment = () => {
2951
+ const menus = [...document.querySelectorAll('[role="menu"]')].filter((m) => m.querySelector('[role="menuitem"]'));
2952
+ const menu = menus[menus.length - 1];
2953
+ if (!menu) {
2954
+ if (++tries < 10) requestAnimationFrame(augment);
2955
+ return;
2956
+ }
2957
+ if (menu.querySelector("[data-rm-ws-menu]")) return;
2958
+ const tpl = menu.querySelector('[role="menuitem"]');
2959
+ const mk = (text, svg, onPick) => {
2960
+ const item = document.createElement("button");
2961
+ item.type = "button";
2962
+ item.setAttribute("role", "menuitem");
2963
+ item.setAttribute("data-rm-ws-menu", text);
2964
+ item.className = tpl.className;
2965
+ item.innerHTML = `<span style="display:inline-flex;width:16px;justify-content:center">${svg}</span><span>${text}</span>`;
2966
+ item.style.display = "flex";
2967
+ item.style.alignItems = "center";
2968
+ item.style.gap = "8px";
2969
+ item.addEventListener("click", (ev) => {
2970
+ ev.preventDefault();
2971
+ ev.stopPropagation();
2972
+ trigger.click();
2973
+ onPick();
2974
+ });
2975
+ return item;
2976
+ };
2977
+ const newSessSvg = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.15" stroke-linecap="round" aria-hidden="true"><path d="M8 3.5v9M3.5 8h9"/></svg>';
2978
+ const first = menu.firstElementChild;
2979
+ menu.insertBefore(mk("New session", newSessSvg, () => {
2980
+ const row2 = document.querySelector(`[data-row-key="workspace:${CSS.escape(key)}"]`);
2981
+ row2?.querySelector('button[aria-label^="New session in"]')?.click();
2982
+ }), first);
2983
+ menu.insertBefore(mk("New room", ROOM_SVG, () => open(key)), first);
2984
+ };
2985
+ requestAnimationFrame(augment);
2986
+ };
2987
+ let raf = 0;
2988
+ const schedule = () => {
2989
+ if (!raf) raf = requestAnimationFrame(() => {
2990
+ raf = 0;
2991
+ decorate();
2992
+ });
2993
+ };
2994
+ const mo = new MutationObserver(schedule);
2995
+ mo.observe(document.body, { childList: true, subtree: true });
2996
+ document.addEventListener("click", onClickCapture, true);
2997
+ schedule();
2998
+ return () => {
2999
+ mo.disconnect();
3000
+ document.removeEventListener("click", onClickCapture, true);
3001
+ if (raf) cancelAnimationFrame(raf);
3002
+ for (const b of document.querySelectorAll("[data-rm-ws-new-room]")) b.remove();
3003
+ };
3004
+ }
2947
3005
  function apply(ctx) {
2948
3006
  const store = createRoomsStore();
3007
+ let native = null;
3008
+ try {
3009
+ native = ctx.get ? ctx.get("workspaces") : ctx.workspaces;
3010
+ } catch {
3011
+ native = ctx.workspaces || null;
3012
+ }
3013
+ store.native = native || null;
3014
+ store.nativeList = native && native.list ? {
3015
+ subscribe: (listener) => native.list.subscribe(listener),
3016
+ getSnapshot: () => native.list.getSnapshot()
3017
+ } : null;
2949
3018
  ctx.effect(() => installStyles(), "dsh-room: styles");
2950
3019
  ctx.effect(() => () => store.dispose(), "dsh-room: store");
2951
3020
  const info = () => ctx.layout.panelInfo;
@@ -2955,27 +3024,12 @@ function apply(ctx) {
2955
3024
  } catch {
2956
3025
  }
2957
3026
  };
3027
+ store.onRoomOpened = ensurePanel;
2958
3028
  ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({ name: "sidebar.panellist", id: PANEL_ID, order: 11, label: () => "DSH Room" }, PanelIcon));
2959
3029
  ctx.slots.inject("main", () => ctx.slots.register({ name: "main", key: PANEL_ID, inject: () => ({ store }) }, RoomsMain));
2960
3030
  ctx.slots.inject("shell.overlay", () => ctx.slots.register({ name: "shell.overlay", id: "dsh-room-dialogs", inject: () => ({ store }) }, Overlay));
2961
- ctx.slots.inject("sidebar.workspaces", () => {
2962
- let dispose;
2963
- const reconcile = () => {
2964
- const active = info().getSnapshot().activePanelId === PANEL_ID;
2965
- if (active && !dispose) dispose = ctx.slots.register({ name: "sidebar.workspaces", priority: -100, inject: () => ({ store, ensurePanel }) }, RoomsSidebar);
2966
- else if (!active && dispose) {
2967
- dispose();
2968
- dispose = void 0;
2969
- }
2970
- };
2971
- const off = info().subscribe(reconcile);
2972
- reconcile();
2973
- return () => {
2974
- off();
2975
- if (dispose) dispose();
2976
- dispose = void 0;
2977
- };
2978
- });
3031
+ ctx.slots.inject("sidebar.session.row.leading", () => ctx.slots.register({ name: "sidebar.session.row.leading", id: "dsh-room-session-mark", inject: () => ({ store }) }, RoomSessionMark));
3032
+ ctx.effect(() => installWorkspaceDecorator(store, native, (workspace) => store.actions.openDialog({ kind: "create", workspace })), "dsh-room: workspace New room");
2979
3033
  }
2980
3034
 
2981
3035
  return module.exports;