@cecwxf/dsh-room 0.1.2 → 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';
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";
@@ -115,7 +115,13 @@ function createRoomsStore() {
115
115
  if (text) p.set("q", text);
116
116
  if (tag) p.set("tag", tag);
117
117
  const r = await call(`/directory?${p}`);
118
- set((s) => s.search.query === query ? { search: { ...s.search, results: r.rooms, loading: false } } : {});
118
+ const rooms = Array.isArray(r.rooms) ? r.rooms.map((x) => ({
119
+ ...x,
120
+ tags: Array.isArray(x.tags) ? x.tags : [],
121
+ members: typeof x.members === "number" ? x.members : 0,
122
+ description: x.description || ""
123
+ })) : [];
124
+ set((s) => s.search.query === query ? { search: { ...s.search, results: rooms, loading: false, error: null } } : {});
119
125
  } catch (e) {
120
126
  set((s) => ({ search: { ...s.search, loading: false, error: e.message } }));
121
127
  }
@@ -147,13 +153,21 @@ function createRoomsStore() {
147
153
  searchTimer = setTimeout(() => void runSearch(query), 300);
148
154
  },
149
155
  async create(form) {
150
- 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
+ }
151
161
  set({ selected: r.roomId, dialog: { kind: "invite", roomId: r.roomId, name: r.name, invite: r.invite, public: r.public, listed: r.listed } });
152
162
  void loadMessages(r.roomId);
153
163
  return r;
154
164
  },
155
165
  async join(invite, info) {
156
- 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
+ }
157
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) } }));
158
172
  void loadMessages(r.roomId);
159
173
  toast(`Joined ${r.name}`);
@@ -203,7 +217,7 @@ function createRoomsStore() {
203
217
  return post(`/rooms/${encodeURIComponent(roomId)}/mcp`, { tool, args, target });
204
218
  }
205
219
  };
206
- return {
220
+ const api = {
207
221
  getSnapshot: () => snap,
208
222
  subscribe(fn) {
209
223
  subs.add(fn);
@@ -216,14 +230,20 @@ function createRoomsStore() {
216
230
  es = null;
217
231
  subs.clear();
218
232
  clearTimeout(searchTimer);
219
- }
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
220
239
  };
240
+ return api;
221
241
  }
222
242
 
223
243
  // src/client/index.jsx
224
244
  var import_jsx_runtime = require("react/jsx-runtime");
225
245
  var name = "dsh-room-client";
226
- var inject = ["slots", "layout"];
246
+ var inject = ["slots", "layout", "workspaces"];
227
247
  var PANEL_ID = "collab";
228
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 });
229
249
  var RoomsIcon = ({ size }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size, children: [
@@ -236,7 +256,6 @@ var SearchIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { child
236
256
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m10.2 10.2 3.3 3.3" })
237
257
  ] });
238
258
  var PlusIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Svg, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 3v10M3 8h10" }) });
239
- 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" }) });
240
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" }) });
241
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" }) });
242
261
  var TaskIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { children: [
@@ -281,209 +300,121 @@ var STATUS_TONE = { open: "info", claimed: "warning", done: "success", failed: "
281
300
  function useStore(store) {
282
301
  return (0, import_react.useSyncExternalStore)(store.subscribe, store.getSnapshot);
283
302
  }
284
- function renderMentions(text) {
285
- 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);
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 || [];
286
311
  }
287
- function PanelIcon({ size }) {
288
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsIcon, { size });
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 };
289
323
  }
290
- function RoomsSidebar({ store, ensurePanel }) {
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
+ ] });
337
+ }
338
+ function RoomsNav({ store }) {
291
339
  const s = useStore(store);
292
340
  const a = store.actions;
293
- const [menu, setMenu] = (0, import_react.useState)(null);
294
- const [searching, setSearching] = (0, import_react.useState)(false);
295
- const [joining, setJoining] = (0, import_react.useState)(null);
296
- const inputRef = (0, import_react.useRef)(null);
297
- const activeWs = (s.workspaces || []).find((w) => w.id === s.activeWorkspaceId) || (s.workspaces || [])[0] || null;
298
- (0, import_react.useEffect)(() => {
299
- if (!menu) return;
300
- const close = () => setMenu(null);
301
- window.addEventListener("click", close);
302
- return () => window.removeEventListener("click", close);
303
- }, [menu]);
304
- (0, import_react.useEffect)(() => {
305
- if (searching && inputRef.current) inputRef.current.focus();
306
- }, [searching]);
307
- const q = (s.search.query || "").trim().toLowerCase();
308
- const mine = (s.rooms || []).filter((r) => {
309
- if (activeWs && r.workspaceId && r.workspaceId !== activeWs.id) return false;
310
- if (!q) return true;
311
- return r.name.toLowerCase().includes(q) || (r.description || "").toLowerCase().includes(q) || (r.tags || []).some((t) => ("#" + t).includes(q));
312
- });
313
- const join = async (r) => {
314
- setJoining(r.roomId);
315
- try {
316
- await a.join(r.invite, { name: r.name, description: r.description, tags: r.tags, public: true, workspaceId: activeWs?.id });
317
- ensurePanel();
318
- } catch (e) {
319
- a.toast(e.message);
320
- } finally {
321
- setJoining(null);
322
- }
323
- };
324
- if (!activeWs) {
325
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-side", "data-collab-sidebar": "", children: [
326
- /* @__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" }) }),
327
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-side-scroll", children: [
328
- /* @__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)." }),
329
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "primary", onClick: () => a.openDialog({ kind: "workspace" }), children: "Add workspace" })
330
- ] })
331
- ] });
332
- }
333
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-side", "data-collab-sidebar": "", children: [
334
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-side-toolbar", children: [
335
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-side-title", children: "DSH Room" }),
336
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-icon-btn", "aria-label": searching ? "Close search" : "Search rooms", "aria-pressed": searching, onClick: () => {
337
- setSearching(!searching);
338
- a.setSearchOpen(!searching);
339
- }, children: searching ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CloseIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}) }),
340
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
341
- "button",
342
- {
343
- type: "button",
344
- className: "rm-icon-btn",
345
- "data-rm-new": "",
346
- "aria-label": "New",
347
- "aria-haspopup": "menu",
348
- "aria-expanded": !!menu,
349
- onClick: (e) => {
350
- e.stopPropagation();
351
- const r = e.currentTarget.getBoundingClientRect();
352
- setMenu(menu ? null : { top: r.bottom + 4, left: Math.max(8, r.right - 200) });
353
- },
354
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {})
355
- }
356
- ),
357
- 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: [
358
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
359
- setMenu(null);
360
- a.openDialog({ kind: "create" });
361
- }, children: [
362
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}),
363
- "Create room in workspace"
364
- ] }),
365
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
366
- setMenu(null);
367
- a.openDialog({ kind: "join" });
368
- }, children: [
369
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkIcon, {}),
370
- "Join by invite"
371
- ] }),
372
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
373
- setMenu(null);
374
- a.openDialog({ kind: "workspace" });
375
- }, children: [
376
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
377
- "Add / switch workspace"
378
- ] }),
379
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
380
- setMenu(null);
381
- setSearching(true);
382
- a.setSearchOpen(true);
383
- }, children: [
384
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
385
- "Browse public rooms"
386
- ] })
387
- ] }) : 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, {}) })
388
352
  ] }),
389
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-bar", children: [
390
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-ws-chip", onClick: () => a.openDialog({ kind: "workspace" }), title: activeWs.path, children: [
391
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
392
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: activeWs.name })
393
- ] }),
394
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-path", title: activeWs.path, children: activeWs.path })
395
- ] }),
396
- searching ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search", children: [
353
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search", children: [
397
354
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
398
- /* @__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" })
399
- ] }) : null,
400
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-side-scroll", children: [
401
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
402
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-group-label", children: [
403
- "Rooms in ",
404
- activeWs.name
405
- ] }),
406
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-room-list", children: [
407
- 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)(
408
363
  "button",
409
364
  {
410
365
  type: "button",
411
- className: "rm-room-item",
412
- "aria-current": r.roomId === s.selected ? "true" : void 0,
413
- "data-room-id": r.roomId,
414
- onClick: () => {
415
- a.select(r.roomId);
416
- ensurePanel();
417
- },
418
- children: [
419
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-avatar-wrap", children: [
420
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: r.name, size: 30 }),
421
- /* @__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)" } })
422
- ] }),
423
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-room-item-body", children: [
424
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-name", children: r.name }),
425
- /* @__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") })
426
- ] }),
427
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-room-item-trail", children: [
428
- r.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge rm-badge-public", children: [
429
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
430
- "Public"
431
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge", children: [
432
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}),
433
- "Private"
434
- ] }),
435
- r.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
436
- r.last ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 10.5, color: "var(--rm-quiet)" }, children: fmtTime(r.last.ts) }) : null
437
- ] })
438
- ]
439
- },
440
- r.roomId
441
- )),
442
- !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
443
- ] })
444
- ] }),
445
- (s.workspaces || []).length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
446
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-group-label", children: "Workspaces" }),
447
- (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: [
448
379
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
449
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-room-item-body", children: [
450
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-name", children: w.name }),
451
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-meta", children: w.path })
452
- ] })
453
- ] }, w.id))
454
- ] }) : null,
455
- searching ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
456
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-group-label", children: [
457
- "Public directory ",
458
- s.search.loading ? "\xB7 searching\u2026" : ""
380
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-name", children: "Other rooms" })
459
381
  ] }),
460
- s.search.results.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-dir-item", "data-dir-room-id": r.roomId, children: [
461
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: r.name, size: 30 }),
462
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-dir-item-body", children: [
463
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-room-item-name", children: r.name }),
464
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-room-item-meta", children: [
465
- r.description || "No description",
466
- " \xB7 ",
467
- r.members,
468
- " online"
469
- ] }),
470
- 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: [
471
- "#",
472
- tg
473
- ] }, tg)) }) : null
474
- ] }),
475
- /* @__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" })
476
- ] }, r.roomId)),
477
- !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,
478
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-empty-hint", children: [
479
- "Joining attaches the room to workspace \u201C",
480
- activeWs.name,
481
- "\u201D. Point your local path at the same project."
482
- ] })
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" }) })
483
387
  ] }) : null
484
388
  ] })
485
389
  ] });
486
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
+ }
487
418
  function Message({ m, showAuthor }) {
488
419
  if (m.kind === "task.create") {
489
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: [
@@ -588,23 +519,23 @@ function MentionMenu({ items, active, onPick, onHover }) {
588
519
  it.id
589
520
  )) });
590
521
  }
591
- function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
522
+ function Composer({ room, store, draft, setDraft }) {
592
523
  const a = store.actions;
593
524
  const [mode, setMode] = (0, import_react.useState)("chat");
594
525
  const [busy, setBusy] = (0, import_react.useState)(false);
595
526
  const [modelOpen, setModelOpen] = (0, import_react.useState)(false);
596
527
  const [permOpen, setPermOpen] = (0, import_react.useState)(false);
597
528
  const [peerOpen, setPeerOpen] = (0, import_react.useState)(false);
598
- const [mentionActive, setMentionActive2] = (0, import_react.useState)(0);
599
- const [caret, setCaret2] = (0, import_react.useState)(0);
529
+ const [mentionActive, setMentionActive] = (0, import_react.useState)(0);
530
+ const [caret, setCaret] = (0, import_react.useState)(0);
600
531
  const [mentionDismissed, setMentionDismissed] = (0, import_react.useState)(false);
601
- const taRef2 = (0, import_react.useRef)(null);
532
+ const taRef = (0, import_react.useRef)(null);
602
533
  const offline = room.status !== "online";
603
- const mqRaw = mentionQueryAt(draft2, caret);
604
- const mq2 = mentionDismissed ? null : mqRaw;
534
+ const mqRaw = mentionQueryAt(draft, caret);
535
+ const mq = mentionDismissed ? null : mqRaw;
605
536
  const mentionItems = (() => {
606
- if (!mq2) return [];
607
- const q = mq2.query.toLowerCase();
537
+ if (!mq) return [];
538
+ const q = mq.query.toLowerCase();
608
539
  const members = (room.members || []).filter((m) => !m.self);
609
540
  const known = members.map((m) => ({
610
541
  id: m.agentId,
@@ -628,7 +559,7 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
628
559
  return bits.length ? `Auto \xB7 ${bits.join("+")}` : "Ask to run";
629
560
  })();
630
561
  (0, import_react.useEffect)(() => {
631
- setMentionActive2(0);
562
+ setMentionActive(0);
632
563
  setMentionDismissed(false);
633
564
  }, [mqRaw && mqRaw.query, mqRaw && mqRaw.start]);
634
565
  const MODELS = ["deepseek-chat", "deepseek-reasoner", "deepseek-coder"];
@@ -651,28 +582,8 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
651
582
  a.toast(e.message);
652
583
  }
653
584
  };
654
- const syncCaret = (el) => {
655
- if (!el) return;
656
- setCaret2(el.selectionStart ?? el.value.length);
657
- };
658
- const insertMention = (it) => {
659
- if (!mq2) return;
660
- const insert = `@${it.id} `;
661
- const next = draft2.slice(0, mq2.start) + insert + draft2.slice(mq2.end);
662
- setDraft2(next);
663
- const pos = mq2.start + insert.length;
664
- setCaret2(pos);
665
- setMentionActive2(0);
666
- requestAnimationFrame(() => {
667
- const el = taRef2.current;
668
- if (el) {
669
- el.focus();
670
- el.setSelectionRange(pos, pos);
671
- }
672
- });
673
- };
674
585
  const submit = async () => {
675
- const text = draft2.trim();
586
+ const text = draft.trim();
676
587
  if (!text || busy) return;
677
588
  setBusy(true);
678
589
  try {
@@ -681,7 +592,7 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
681
592
  await a.task(room.roomId, m ? text.slice(m[0].length) : text, m ? m[1] : void 0);
682
593
  setMode("chat");
683
594
  } else await a.send(room.roomId, text);
684
- setDraft2("");
595
+ setDraft("");
685
596
  } catch (e) {
686
597
  a.toast(e.message);
687
598
  } finally {
@@ -690,33 +601,33 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
690
601
  };
691
602
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-composer-card", "data-rm-composer": "", children: [
692
603
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-composer-grow", children: [
693
- mq2 && mentionItems.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MentionMenu, { items: mentionItems, active: mentionActive, onHover: setMentionActive2, onPick: insertMention }) : null,
604
+ mq && mentionItems.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MentionMenu, { items: mentionItems, active: mentionActive, onHover: setMentionActive, onPick: insertMention }) : null,
694
605
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
695
606
  "textarea",
696
607
  {
697
- ref: taRef2,
608
+ ref: taRef,
698
609
  rows: 1,
699
610
  disabled: offline,
700
611
  "aria-label": "Message the room",
701
612
  placeholder: offline ? "Room is offline" : mode === "task" ? "Task: @assignee title (optional assignee)" : `Message ${room.name} \xB7 @agent or @all`,
702
- value: draft2,
613
+ value: draft,
703
614
  onChange: (e) => {
704
- setDraft2(e.target.value);
615
+ setDraft(e.target.value);
705
616
  syncCaret(e.target);
706
617
  },
707
618
  onClick: (e) => syncCaret(e.target),
708
619
  onKeyUp: (e) => syncCaret(e.target),
709
620
  onSelect: (e) => syncCaret(e.target),
710
621
  onKeyDown: (e) => {
711
- if (mq2 && mentionItems.length) {
622
+ if (mq && mentionItems.length) {
712
623
  if (e.key === "ArrowDown") {
713
624
  e.preventDefault();
714
- setMentionActive2((i) => (i + 1) % mentionItems.length);
625
+ setMentionActive((i) => (i + 1) % mentionItems.length);
715
626
  return;
716
627
  }
717
628
  if (e.key === "ArrowUp") {
718
629
  e.preventDefault();
719
- setMentionActive2((i) => (i - 1 + mentionItems.length) % mentionItems.length);
630
+ setMentionActive((i) => (i - 1 + mentionItems.length) % mentionItems.length);
720
631
  return;
721
632
  }
722
633
  if (e.key === "Enter" || e.key === "Tab") {
@@ -802,7 +713,7 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
802
713
  peerOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PeerGrantsMenu, { room, store, onClose: () => setPeerOpen(false), patchConfig }) : null
803
714
  ] })
804
715
  ] }),
805
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-composer-trailing", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-composer-send", "aria-label": mode === "task" ? "Create task" : "Send", disabled: !draft2.trim() || busy || offline, onClick: submit, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SendIcon, {}) }) })
716
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-composer-trailing", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-composer-send", "aria-label": mode === "task" ? "Create task" : "Send", disabled: !draft.trim() || busy || offline, onClick: submit, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SendIcon, {}) }) })
806
717
  ] })
807
718
  ] });
808
719
  }
@@ -939,39 +850,33 @@ function RoomDetail({ room, messages, store, mention, assign }) {
939
850
  ] });
940
851
  }
941
852
  function EmptyMain({ store }) {
942
- const s = useStore(store);
943
853
  const a = store.actions;
944
- 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: [
945
857
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-badge", children: "Collab \xB7 E2EE \xB7 no accounts" }),
946
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: "Workspaces, then rooms" }),
947
- /* @__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." }),
948
860
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-welcome-actions", children: [
949
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "workspace" }), children: [
950
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
951
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
952
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Bind workspace" }),
953
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Local project path shared by agents in rooms" })
954
- ] })
955
- ] }),
956
- /* @__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: [
957
862
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}),
958
863
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
959
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Create room" }),
960
- /* @__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" })
961
866
  ] })
962
867
  ] }),
963
868
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "join" }), children: [
964
869
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkIcon, {}),
965
870
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
966
871
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Join by invite" }),
967
- /* @__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" })
968
873
  ] })
969
874
  ] }),
970
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.setSearchOpen(true), children: [
875
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "browse" }), children: [
971
876
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
972
877
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
973
878
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Browse public rooms" }),
974
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Search the relay directory by name or #tag" })
879
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Search the VPS directory by name or #tag" })
975
880
  ] })
976
881
  ] })
977
882
  ] })
@@ -981,51 +886,60 @@ function RoomsMain({ store }) {
981
886
  const s = useStore(store);
982
887
  const [drafts, setDrafts] = (0, import_react.useState)({});
983
888
  const room = s.rooms.find((r) => r.roomId === s.selected);
984
- 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
+ ] });
985
896
  const messages = s.messages[room.roomId] || [];
986
- const draft2 = drafts[room.roomId] || "";
987
- const setDraft2 = (v) => setDrafts((d) => ({ ...d, [room.roomId]: typeof v === "function" ? v(d[room.roomId] || "") : v }));
897
+ const draft = drafts[room.roomId] || "";
898
+ const setDraft = (v) => setDrafts((d) => ({ ...d, [room.roomId]: typeof v === "function" ? v(d[room.roomId] || "") : v }));
988
899
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-main", "data-collab-main": "", children: [
989
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApprovalBanner, { store }),
990
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-workspace", children: [
991
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "rm-chat", children: [
992
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "rm-chat-header", children: [
993
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: room.name, size: 28 }),
994
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
995
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-chat-header-title", children: room.name }),
996
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-chat-header-sub", children: [
997
- room.members.filter((m) => m.online !== false).length,
998
- " online \xB7 ",
999
- room.public ? "public" : "private",
1000
- room.status !== "online" ? ` \xB7 room ${room.status}` : ""
1001
- ] })
1002
- ] }),
1003
- room.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge rm-badge-public", children: [
1004
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
1005
- "Public"
1006
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge", children: [
1007
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}),
1008
- "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 })
1009
925
  ] }),
1010
- room.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
1011
- /* @__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 })
1012
928
  ] }),
1013
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages }),
1014
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Composer, { room, store, draft: draft2, setDraft: setDraft2 })
1015
- ] }),
1016
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1017
- RoomDetail,
1018
- {
1019
- room,
1020
- messages,
1021
- store,
1022
- mention: (id) => setDraft2((d) => `${d}${d && !d.endsWith(" ") ? " " : ""}@${id} `),
1023
- assign: (id) => {
1024
- setDraft2(`@${id} `);
1025
- 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
+ }
1026
940
  }
1027
- }
1028
- )
941
+ )
942
+ ] })
1029
943
  ] })
1030
944
  ] });
1031
945
  }
@@ -1036,7 +950,7 @@ function Field({ label, hint, children, htmlFor }) {
1036
950
  hint ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: hint }) : null
1037
951
  ] });
1038
952
  }
1039
- function CreateDialog({ store }) {
953
+ function CreateDialog({ store, d }) {
1040
954
  const s = useStore(store);
1041
955
  const a = store.actions;
1042
956
  const [f, setF] = (0, import_react.useState)({ name: "", description: "", tags: "", public: false, mode: "lan", lanHostPort: "62.234.113.56:80" });
@@ -1044,28 +958,13 @@ function CreateDialog({ store }) {
1044
958
  const [busy, setBusy] = (0, import_react.useState)(false);
1045
959
  const [lanPeers, setLanPeers] = (0, import_react.useState)([]);
1046
960
  const ok = f.name.trim() && !busy;
1047
- const syncCaret = (el) => {
1048
- if (!el) return;
1049
- setCaret(el.selectionStart ?? el.value.length);
1050
- };
1051
- const insertMention = (it) => {
1052
- if (!mq) return;
1053
- const insert = `@${it.id} `;
1054
- const next = draft.slice(0, mq.start) + insert + draft.slice(mq.end);
1055
- setDraft(next);
1056
- const pos = mq.start + insert.length;
1057
- setCaret(pos);
1058
- setMentionActive(0);
1059
- requestAnimationFrame(() => {
1060
- const el = taRef.current;
1061
- if (el) {
1062
- el.focus();
1063
- el.setSelectionRange(pos, pos);
1064
- }
1065
- });
1066
- };
961
+ const [ws, setWs] = (0, import_react.useState)(d && d.workspace ? d.workspace : null);
1067
962
  const submit = async () => {
1068
963
  if (!ok) return;
964
+ if (!ws) {
965
+ setErr("Pick a workspace");
966
+ return;
967
+ }
1069
968
  setBusy(true);
1070
969
  setErr(null);
1071
970
  try {
@@ -1075,7 +974,9 @@ function CreateDialog({ store }) {
1075
974
  tags: f.tags,
1076
975
  public: f.public,
1077
976
  mode: f.mode,
1078
- 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
1079
980
  });
1080
981
  } catch (e) {
1081
982
  setErr(e.message);
@@ -1097,23 +998,25 @@ function CreateDialog({ store }) {
1097
998
  onClose: a.closeDialog,
1098
999
  title: "Create room",
1099
1000
  closeLabel: "Close",
1100
- description: "Room is created inside the active workspace (path becomes MCP sandbox). Default transport is the VPS Node relay (62.234.113.56:80) with optional public directory. Cloudflare remains an advanced option. The room key never goes to the relay.",
1001
+ className: "rm-modal-wide",
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.",
1101
1003
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1102
1004
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
1103
1005
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: !ok, onClick: submit, children: busy ? "Creating\u2026" : "Create" })
1104
1006
  ] }),
1105
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form", onKeyDown: (e) => e.key === "Enter" && e.target.tagName === "INPUT" && submit(), children: [
1106
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Transport", hint: "Default: VPS Node relay at 62.234.113.56:80 (invite-only or public directory). Blank host = embedded local LAN + mDNS. Cloudflare is optional.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
1107
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.mode === "lan" ? " is-on" : ""), onClick: () => setF({ ...f, mode: "lan", lanHostPort: f.lanHostPort || "62.234.113.56:80" }), children: "LAN / VPS" }),
1108
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.mode === "relay" ? " is-on" : ""), onClick: () => setF({ ...f, mode: "relay" }), children: "Cloudflare (advanced)" })
1109
- ] }) }),
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 }) }),
1110
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: [
1111
1010
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: f.name || "room", size: 36 }),
1112
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
1012
  ] }) }),
1114
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Description (optional)", htmlFor: "rm-create-desc", hint: "Shown to members and, if public, in search results.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-desc", placeholder: "e.g. Agents that review and ship releases", maxLength: 280, value: f.description, onChange: (e) => setF({ ...f, description: e.target.value }) }) }),
1115
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Tags (optional)", htmlFor: "rm-create-tags", hint: "Spaces or commas, up to 8. Used by public room search.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-tags", placeholder: "e.g. review rust", value: f.tags, onChange: (e) => setF({ ...f, tags: e.target.value }) }) }),
1116
- f.mode === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, { label: "Relay host:port", htmlFor: "rm-lan-hp", hint: "Default VPS 62.234.113.56:80 (public directory works). Blank = this machine (LAN/Tailscale + mDNS). Invite: collab://lan/host:port/room#key.", children: [
1013
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Transport", hint: "VPS directory works on LAN/VPS. Cloudflare is optional.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
1014
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.mode === "lan" ? " is-on" : ""), onClick: () => setF({ ...f, mode: "lan", lanHostPort: f.lanHostPort || "62.234.113.56:80" }), children: "LAN / VPS" }),
1015
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.mode === "relay" ? " is-on" : ""), onClick: () => setF({ ...f, mode: "relay" }), children: "Cloudflare" })
1016
+ ] }) }),
1017
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Description", htmlFor: "rm-create-desc", hint: "Shown to members and in public search.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-desc", placeholder: "e.g. Agents that review and ship releases", maxLength: 280, value: f.description, onChange: (e) => setF({ ...f, description: e.target.value }) }) }),
1018
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Tags", htmlFor: "rm-create-tags", hint: "Spaces or commas, up to 8.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-tags", placeholder: "e.g. review rust", value: f.tags, onChange: (e) => setF({ ...f, tags: e.target.value }) }) }),
1019
+ f.mode === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, { label: "Relay host:port", htmlFor: "rm-lan-hp", hint: "Default 62.234.113.56:80. Blank = this machine + mDNS.", children: [
1117
1020
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-lan-hp", placeholder: "62.234.113.56:80 or 100.x.x.x:7421", value: f.lanHostPort, onChange: (e) => setF({ ...f, lanHostPort: e.target.value }) }),
1118
1021
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-lan-browse", children: [
1119
1022
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: browseLan, children: "Browse mDNS" }),
@@ -1122,15 +1025,15 @@ function CreateDialog({ store }) {
1122
1025
  p.name ? ` \xB7 ${p.name}` : ""
1123
1026
  ] }, p.hostPort))
1124
1027
  ] })
1125
- ] }) : null,
1126
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
1028
+ ] }) }) : null,
1029
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-switch-row", children: [
1127
1030
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1128
1031
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "List in public directory" }),
1129
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: f.public ? "Anyone can find this room by name or tag and join it. Its key is published with the listing \u2014 NOT confidential." : "Private: invite only. Nothing about the room is published." })
1032
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: f.public ? "Anyone can find and join \u2014 key is published with the listing." : "Private: invite only." })
1130
1033
  ] }),
1131
1034
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.public, onChange: (v) => setF({ ...f, public: v }), label: "List in public directory" })
1132
1035
  ] }),
1133
- err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: err }) : null
1036
+ err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-error", children: err }) : null
1134
1037
  ] })
1135
1038
  }
1136
1039
  );
@@ -1142,32 +1045,13 @@ function JoinDialog({ store }) {
1142
1045
  const [err, setErr] = (0, import_react.useState)(null);
1143
1046
  const [busy, setBusy] = (0, import_react.useState)(false);
1144
1047
  const valid = /^collab:\/\/(join|lan|relay)\/.+#\S+$/.test(invite.trim());
1145
- const syncCaret = (el) => {
1146
- if (!el) return;
1147
- setCaret(el.selectionStart ?? el.value.length);
1148
- };
1149
- const insertMention = (it) => {
1150
- if (!mq) return;
1151
- const insert = `@${it.id} `;
1152
- const next = draft.slice(0, mq.start) + insert + draft.slice(mq.end);
1153
- setDraft(next);
1154
- const pos = mq.start + insert.length;
1155
- setCaret(pos);
1156
- setMentionActive(0);
1157
- requestAnimationFrame(() => {
1158
- const el = taRef.current;
1159
- if (el) {
1160
- el.focus();
1161
- el.setSelectionRange(pos, pos);
1162
- }
1163
- });
1164
- };
1048
+ const [ws, setWs] = (0, import_react.useState)(null);
1165
1049
  const submit = async () => {
1166
1050
  if (!valid || busy) return;
1167
1051
  setBusy(true);
1168
1052
  setErr(null);
1169
1053
  try {
1170
- await a.join(invite.trim());
1054
+ await a.join(invite.trim(), ws ? { workspacePath: ws.path, workspaceName: ws.title } : void 0);
1171
1055
  } catch (e) {
1172
1056
  setErr(e.message);
1173
1057
  setBusy(false);
@@ -1180,12 +1064,13 @@ function JoinDialog({ store }) {
1180
1064
  onClose: a.closeDialog,
1181
1065
  title: "Join room",
1182
1066
  closeLabel: "Close",
1183
- 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.",
1184
1068
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1185
1069
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
1186
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" })
1187
1071
  ] }),
1188
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 }),
1189
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() }),
1190
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,
1191
1076
  err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: err }) : null
@@ -1266,26 +1151,6 @@ function ConfigDialog({ store, d }) {
1266
1151
  a.getConfig(d.roomId).then((c) => setF((x) => ({ ...x, workspacePath: c.workspacePath || "", workspaceLabel: c.workspaceLabel || x.workspaceLabel, model: c.model || "", provider: c.provider || "", reasoningEffort: c.reasoningEffort || "medium", autoRead: c.permissions?.autoRead !== false, autoWrite: !!c.permissions?.autoWrite, autoShell: !!c.permissions?.autoShell, autoNetwork: !!c.permissions?.autoNetwork, mcpEnabled: c.mcpEnabled !== false }))).catch(() => {
1267
1152
  });
1268
1153
  }, [d.roomId]);
1269
- const syncCaret = (el) => {
1270
- if (!el) return;
1271
- setCaret(el.selectionStart ?? el.value.length);
1272
- };
1273
- const insertMention = (it) => {
1274
- if (!mq) return;
1275
- const insert = `@${it.id} `;
1276
- const next = draft.slice(0, mq.start) + insert + draft.slice(mq.end);
1277
- setDraft(next);
1278
- const pos = mq.start + insert.length;
1279
- setCaret(pos);
1280
- setMentionActive(0);
1281
- requestAnimationFrame(() => {
1282
- const el = taRef.current;
1283
- if (el) {
1284
- el.focus();
1285
- el.setSelectionRange(pos, pos);
1286
- }
1287
- });
1288
- };
1289
1154
  const submit = async () => {
1290
1155
  setBusy(true);
1291
1156
  setErr(null);
@@ -1387,26 +1252,6 @@ function WorkspaceDialog({ store }) {
1387
1252
  const [path, setPath] = (0, import_react.useState)("");
1388
1253
  const [err, setErr] = (0, import_react.useState)(null);
1389
1254
  const [busy, setBusy] = (0, import_react.useState)(false);
1390
- const syncCaret = (el) => {
1391
- if (!el) return;
1392
- setCaret(el.selectionStart ?? el.value.length);
1393
- };
1394
- const insertMention = (it) => {
1395
- if (!mq) return;
1396
- const insert = `@${it.id} `;
1397
- const next = draft.slice(0, mq.start) + insert + draft.slice(mq.end);
1398
- setDraft(next);
1399
- const pos = mq.start + insert.length;
1400
- setCaret(pos);
1401
- setMentionActive(0);
1402
- requestAnimationFrame(() => {
1403
- const el = taRef.current;
1404
- if (el) {
1405
- el.focus();
1406
- el.setSelectionRange(pos, pos);
1407
- }
1408
- });
1409
- };
1410
1255
  const submit = async () => {
1411
1256
  if (!path.trim()) {
1412
1257
  setErr("Absolute workspace path required");
@@ -1415,9 +1260,10 @@ function WorkspaceDialog({ store }) {
1415
1260
  setBusy(true);
1416
1261
  setErr(null);
1417
1262
  try {
1418
- 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() });
1419
1265
  a.closeDialog();
1420
- 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");
1421
1267
  } catch (e) {
1422
1268
  setErr(e.message);
1423
1269
  setBusy(false);
@@ -1428,18 +1274,14 @@ function WorkspaceDialog({ store }) {
1428
1274
  {
1429
1275
  open: true,
1430
1276
  onClose: a.closeDialog,
1431
- title: "Workspace",
1277
+ title: "Add workspace",
1432
1278
  closeLabel: "Close",
1433
- 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.",
1434
1280
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1435
1281
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
1436
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" })
1437
1283
  ] }),
1438
1284
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form", children: [
1439
- (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 () => {
1440
- await a.setActiveWorkspace(w.id);
1441
- a.closeDialog();
1442
- }, children: w.name }, w.id)) }) }) : null,
1443
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) }) }),
1444
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() }) }),
1445
1287
  err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: err }) : null
@@ -1447,16 +1289,90 @@ function WorkspaceDialog({ store }) {
1447
1289
  }
1448
1290
  );
1449
1291
  }
1292
+ function BrowseDialog({ store }) {
1293
+ const s = useStore(store);
1294
+ const a = store.actions;
1295
+ const [joining, setJoining] = (0, import_react.useState)(null);
1296
+ const inputRef = (0, import_react.useRef)(null);
1297
+ const [activeWs, setActiveWs] = (0, import_react.useState)(null);
1298
+ (0, import_react.useEffect)(() => {
1299
+ a.setSearchOpen(true);
1300
+ requestAnimationFrame(() => inputRef.current?.focus());
1301
+ return () => {
1302
+ };
1303
+ }, []);
1304
+ const join = async (r) => {
1305
+ setJoining(r.roomId);
1306
+ try {
1307
+ await a.join(r.invite, { name: r.name, description: r.description, tags: r.tags, public: true, workspacePath: activeWs?.path, workspaceName: activeWs?.title });
1308
+ } catch (e) {
1309
+ a.toast(e.message);
1310
+ } finally {
1311
+ setJoining(null);
1312
+ }
1313
+ };
1314
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1315
+ import_dsh_client_ui_primitives.Modal,
1316
+ {
1317
+ open: true,
1318
+ onClose: a.closeDialog,
1319
+ title: "Browse public rooms",
1320
+ closeLabel: "Close",
1321
+ className: "rm-modal-wide",
1322
+ description: "Directory on VPS 62.234.113.56:80. Search by name or #tag; joined rooms attach to the workspace below.",
1323
+ footer: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Close" }),
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 }),
1326
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search rm-browse-search", children: [
1327
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
1328
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1329
+ "input",
1330
+ {
1331
+ ref: inputRef,
1332
+ type: "text",
1333
+ placeholder: "Search rooms or #tags",
1334
+ value: s.search.query,
1335
+ onChange: (e) => a.setQuery(e.target.value),
1336
+ "aria-label": "Search public rooms"
1337
+ }
1338
+ ),
1339
+ s.search.loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-browse-status", children: "Searching\u2026" }) : null
1340
+ ] }),
1341
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-browse-list", children: [
1342
+ s.search.results.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-dir-item", "data-dir-room-id": r.roomId, children: [
1343
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: r.name, size: 30 }),
1344
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-dir-item-body", children: [
1345
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-room-item-name", children: r.name }),
1346
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-room-item-meta", children: [
1347
+ r.description || "No description",
1348
+ " \xB7 ",
1349
+ r.members,
1350
+ " online"
1351
+ ] }),
1352
+ (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: [
1353
+ "#",
1354
+ tg
1355
+ ] }, tg)) }) : null
1356
+ ] }),
1357
+ /* @__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 || !activeWs, onClick: () => join(r), children: !activeWs ? "Need workspace" : r.joined ? "Joined" : joining === r.roomId ? "\u2026" : "Join" })
1358
+ ] }, r.roomId)),
1359
+ !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 listed yet. Create a room with \u201CList in public directory\u201D on, then refresh here." }) : null
1360
+ ] })
1361
+ ] })
1362
+ }
1363
+ );
1364
+ }
1450
1365
  function Overlay({ store }) {
1451
1366
  const s = useStore(store);
1452
1367
  const d = s.dialog;
1453
1368
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1454
- 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,
1455
1370
  d?.kind === "join" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JoinDialog, { store }) : null,
1456
1371
  d?.kind === "invite" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InviteDialog, { store, d }) : null,
1457
1372
  d?.kind === "leave" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LeaveDialog, { store, d }) : null,
1458
1373
  d?.kind === "config" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConfigDialog, { store, d }) : null,
1459
1374
  d?.kind === "workspace" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceDialog, { store }) : null,
1375
+ d?.kind === "browse" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BrowseDialog, { store }) : null,
1460
1376
  s.toast ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Toast, { text: s.toast, onDone: store.actions.clearToast, holdMs: 2200 }) : null
1461
1377
  ] });
1462
1378
  }
@@ -1469,8 +1385,118 @@ function installStyles() {
1469
1385
  document.head.appendChild(style);
1470
1386
  return () => style.remove();
1471
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
+ }
1472
1487
  function apply(ctx) {
1473
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;
1474
1500
  ctx.effect(() => installStyles(), "dsh-room: styles");
1475
1501
  ctx.effect(() => () => store.dispose(), "dsh-room: store");
1476
1502
  const info = () => ctx.layout.panelInfo;
@@ -1480,27 +1506,12 @@ function apply(ctx) {
1480
1506
  } catch {
1481
1507
  }
1482
1508
  };
1509
+ store.onRoomOpened = ensurePanel;
1483
1510
  ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({ name: "sidebar.panellist", id: PANEL_ID, order: 11, label: () => "DSH Room" }, PanelIcon));
1484
1511
  ctx.slots.inject("main", () => ctx.slots.register({ name: "main", key: PANEL_ID, inject: () => ({ store }) }, RoomsMain));
1485
1512
  ctx.slots.inject("shell.overlay", () => ctx.slots.register({ name: "shell.overlay", id: "dsh-room-dialogs", inject: () => ({ store }) }, Overlay));
1486
- ctx.slots.inject("sidebar.workspaces", () => {
1487
- let dispose;
1488
- const reconcile = () => {
1489
- const active = info().getSnapshot().activePanelId === PANEL_ID;
1490
- if (active && !dispose) dispose = ctx.slots.register({ name: "sidebar.workspaces", priority: -100, inject: () => ({ store, ensurePanel }) }, RoomsSidebar);
1491
- else if (!active && dispose) {
1492
- dispose();
1493
- dispose = void 0;
1494
- }
1495
- };
1496
- const off = info().subscribe(reconcile);
1497
- reconcile();
1498
- return () => {
1499
- off();
1500
- if (dispose) dispose();
1501
- dispose = void 0;
1502
- };
1503
- });
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");
1504
1515
  }
1505
1516
 
1506
1517
  return module.exports;
@@ -1544,7 +1555,7 @@ var import_react = require("react");
1544
1555
  var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
1545
1556
 
1546
1557
  // src/client/rooms.css
1547
- 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';
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';
1548
1559
 
1549
1560
  // src/client/store.js
1550
1561
  var API = "/api/collab";
@@ -1622,7 +1633,13 @@ function createRoomsStore() {
1622
1633
  if (text) p.set("q", text);
1623
1634
  if (tag) p.set("tag", tag);
1624
1635
  const r = await call(`/directory?${p}`);
1625
- set((s) => s.search.query === query ? { search: { ...s.search, results: r.rooms, loading: false } } : {});
1636
+ const rooms = Array.isArray(r.rooms) ? r.rooms.map((x) => ({
1637
+ ...x,
1638
+ tags: Array.isArray(x.tags) ? x.tags : [],
1639
+ members: typeof x.members === "number" ? x.members : 0,
1640
+ description: x.description || ""
1641
+ })) : [];
1642
+ set((s) => s.search.query === query ? { search: { ...s.search, results: rooms, loading: false, error: null } } : {});
1626
1643
  } catch (e) {
1627
1644
  set((s) => ({ search: { ...s.search, loading: false, error: e.message } }));
1628
1645
  }
@@ -1654,13 +1671,21 @@ function createRoomsStore() {
1654
1671
  searchTimer = setTimeout(() => void runSearch(query), 300);
1655
1672
  },
1656
1673
  async create(form) {
1657
- 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
+ }
1658
1679
  set({ selected: r.roomId, dialog: { kind: "invite", roomId: r.roomId, name: r.name, invite: r.invite, public: r.public, listed: r.listed } });
1659
1680
  void loadMessages(r.roomId);
1660
1681
  return r;
1661
1682
  },
1662
1683
  async join(invite, info) {
1663
- 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
+ }
1664
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) } }));
1665
1690
  void loadMessages(r.roomId);
1666
1691
  toast(`Joined ${r.name}`);
@@ -1710,7 +1735,7 @@ function createRoomsStore() {
1710
1735
  return post(`/rooms/${encodeURIComponent(roomId)}/mcp`, { tool, args, target });
1711
1736
  }
1712
1737
  };
1713
- return {
1738
+ const api = {
1714
1739
  getSnapshot: () => snap,
1715
1740
  subscribe(fn) {
1716
1741
  subs.add(fn);
@@ -1723,14 +1748,20 @@ function createRoomsStore() {
1723
1748
  es = null;
1724
1749
  subs.clear();
1725
1750
  clearTimeout(searchTimer);
1726
- }
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
1727
1757
  };
1758
+ return api;
1728
1759
  }
1729
1760
 
1730
1761
  // src/client/index.jsx
1731
1762
  var import_jsx_runtime = require("react/jsx-runtime");
1732
1763
  var name = "dsh-room-client";
1733
- var inject = ["slots", "layout"];
1764
+ var inject = ["slots", "layout", "workspaces"];
1734
1765
  var PANEL_ID = "collab";
1735
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 });
1736
1767
  var RoomsIcon = ({ size }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size, children: [
@@ -1743,7 +1774,6 @@ var SearchIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { child
1743
1774
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m10.2 10.2 3.3 3.3" })
1744
1775
  ] });
1745
1776
  var PlusIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Svg, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 3v10M3 8h10" }) });
1746
- 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" }) });
1747
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" }) });
1748
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" }) });
1749
1779
  var TaskIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { children: [
@@ -1788,209 +1818,121 @@ var STATUS_TONE = { open: "info", claimed: "warning", done: "success", failed: "
1788
1818
  function useStore(store) {
1789
1819
  return (0, import_react.useSyncExternalStore)(store.subscribe, store.getSnapshot);
1790
1820
  }
1791
- function renderMentions(text) {
1792
- 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);
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 || [];
1793
1829
  }
1794
- function PanelIcon({ size }) {
1795
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsIcon, { size });
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 };
1796
1841
  }
1797
- function RoomsSidebar({ store, ensurePanel }) {
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
+ ] });
1855
+ }
1856
+ function RoomsNav({ store }) {
1798
1857
  const s = useStore(store);
1799
1858
  const a = store.actions;
1800
- const [menu, setMenu] = (0, import_react.useState)(null);
1801
- const [searching, setSearching] = (0, import_react.useState)(false);
1802
- const [joining, setJoining] = (0, import_react.useState)(null);
1803
- const inputRef = (0, import_react.useRef)(null);
1804
- const activeWs = (s.workspaces || []).find((w) => w.id === s.activeWorkspaceId) || (s.workspaces || [])[0] || null;
1805
- (0, import_react.useEffect)(() => {
1806
- if (!menu) return;
1807
- const close = () => setMenu(null);
1808
- window.addEventListener("click", close);
1809
- return () => window.removeEventListener("click", close);
1810
- }, [menu]);
1811
- (0, import_react.useEffect)(() => {
1812
- if (searching && inputRef.current) inputRef.current.focus();
1813
- }, [searching]);
1814
- const q = (s.search.query || "").trim().toLowerCase();
1815
- const mine = (s.rooms || []).filter((r) => {
1816
- if (activeWs && r.workspaceId && r.workspaceId !== activeWs.id) return false;
1817
- if (!q) return true;
1818
- return r.name.toLowerCase().includes(q) || (r.description || "").toLowerCase().includes(q) || (r.tags || []).some((t) => ("#" + t).includes(q));
1819
- });
1820
- const join = async (r) => {
1821
- setJoining(r.roomId);
1822
- try {
1823
- await a.join(r.invite, { name: r.name, description: r.description, tags: r.tags, public: true, workspaceId: activeWs?.id });
1824
- ensurePanel();
1825
- } catch (e) {
1826
- a.toast(e.message);
1827
- } finally {
1828
- setJoining(null);
1829
- }
1830
- };
1831
- if (!activeWs) {
1832
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-side", "data-collab-sidebar": "", children: [
1833
- /* @__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" }) }),
1834
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-side-scroll", children: [
1835
- /* @__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)." }),
1836
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "primary", onClick: () => a.openDialog({ kind: "workspace" }), children: "Add workspace" })
1837
- ] })
1838
- ] });
1839
- }
1840
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-side", "data-collab-sidebar": "", children: [
1841
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-side-toolbar", children: [
1842
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-side-title", children: "DSH Room" }),
1843
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-icon-btn", "aria-label": searching ? "Close search" : "Search rooms", "aria-pressed": searching, onClick: () => {
1844
- setSearching(!searching);
1845
- a.setSearchOpen(!searching);
1846
- }, children: searching ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CloseIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}) }),
1847
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1848
- "button",
1849
- {
1850
- type: "button",
1851
- className: "rm-icon-btn",
1852
- "data-rm-new": "",
1853
- "aria-label": "New",
1854
- "aria-haspopup": "menu",
1855
- "aria-expanded": !!menu,
1856
- onClick: (e) => {
1857
- e.stopPropagation();
1858
- const r = e.currentTarget.getBoundingClientRect();
1859
- setMenu(menu ? null : { top: r.bottom + 4, left: Math.max(8, r.right - 200) });
1860
- },
1861
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {})
1862
- }
1863
- ),
1864
- 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: [
1865
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
1866
- setMenu(null);
1867
- a.openDialog({ kind: "create" });
1868
- }, children: [
1869
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}),
1870
- "Create room in workspace"
1871
- ] }),
1872
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
1873
- setMenu(null);
1874
- a.openDialog({ kind: "join" });
1875
- }, children: [
1876
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkIcon, {}),
1877
- "Join by invite"
1878
- ] }),
1879
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
1880
- setMenu(null);
1881
- a.openDialog({ kind: "workspace" });
1882
- }, children: [
1883
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
1884
- "Add / switch workspace"
1885
- ] }),
1886
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
1887
- setMenu(null);
1888
- setSearching(true);
1889
- a.setSearchOpen(true);
1890
- }, children: [
1891
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
1892
- "Browse public rooms"
1893
- ] })
1894
- ] }) : 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, {}) })
1895
1870
  ] }),
1896
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-bar", children: [
1897
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-ws-chip", onClick: () => a.openDialog({ kind: "workspace" }), title: activeWs.path, children: [
1898
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
1899
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: activeWs.name })
1900
- ] }),
1901
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-path", title: activeWs.path, children: activeWs.path })
1902
- ] }),
1903
- searching ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search", children: [
1871
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search", children: [
1904
1872
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
1905
- /* @__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" })
1906
- ] }) : null,
1907
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-side-scroll", children: [
1908
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1909
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-group-label", children: [
1910
- "Rooms in ",
1911
- activeWs.name
1912
- ] }),
1913
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-room-list", children: [
1914
- 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)(
1915
1881
  "button",
1916
1882
  {
1917
1883
  type: "button",
1918
- className: "rm-room-item",
1919
- "aria-current": r.roomId === s.selected ? "true" : void 0,
1920
- "data-room-id": r.roomId,
1921
- onClick: () => {
1922
- a.select(r.roomId);
1923
- ensurePanel();
1924
- },
1925
- children: [
1926
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-avatar-wrap", children: [
1927
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: r.name, size: 30 }),
1928
- /* @__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)" } })
1929
- ] }),
1930
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-room-item-body", children: [
1931
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-name", children: r.name }),
1932
- /* @__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") })
1933
- ] }),
1934
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-room-item-trail", children: [
1935
- r.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge rm-badge-public", children: [
1936
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
1937
- "Public"
1938
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge", children: [
1939
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}),
1940
- "Private"
1941
- ] }),
1942
- r.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
1943
- r.last ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 10.5, color: "var(--rm-quiet)" }, children: fmtTime(r.last.ts) }) : null
1944
- ] })
1945
- ]
1946
- },
1947
- r.roomId
1948
- )),
1949
- !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
1950
- ] })
1951
- ] }),
1952
- (s.workspaces || []).length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1953
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-group-label", children: "Workspaces" }),
1954
- (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: [
1955
1897
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
1956
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-room-item-body", children: [
1957
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-name", children: w.name }),
1958
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-meta", children: w.path })
1959
- ] })
1960
- ] }, w.id))
1961
- ] }) : null,
1962
- searching ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1963
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-group-label", children: [
1964
- "Public directory ",
1965
- s.search.loading ? "\xB7 searching\u2026" : ""
1898
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-name", children: "Other rooms" })
1966
1899
  ] }),
1967
- s.search.results.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-dir-item", "data-dir-room-id": r.roomId, children: [
1968
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: r.name, size: 30 }),
1969
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-dir-item-body", children: [
1970
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-room-item-name", children: r.name }),
1971
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-room-item-meta", children: [
1972
- r.description || "No description",
1973
- " \xB7 ",
1974
- r.members,
1975
- " online"
1976
- ] }),
1977
- 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: [
1978
- "#",
1979
- tg
1980
- ] }, tg)) }) : null
1981
- ] }),
1982
- /* @__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" })
1983
- ] }, r.roomId)),
1984
- !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,
1985
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-empty-hint", children: [
1986
- "Joining attaches the room to workspace \u201C",
1987
- activeWs.name,
1988
- "\u201D. Point your local path at the same project."
1989
- ] })
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" }) })
1990
1905
  ] }) : null
1991
1906
  ] })
1992
1907
  ] });
1993
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
+ }
1994
1936
  function Message({ m, showAuthor }) {
1995
1937
  if (m.kind === "task.create") {
1996
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: [
@@ -2095,23 +2037,23 @@ function MentionMenu({ items, active, onPick, onHover }) {
2095
2037
  it.id
2096
2038
  )) });
2097
2039
  }
2098
- function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
2040
+ function Composer({ room, store, draft, setDraft }) {
2099
2041
  const a = store.actions;
2100
2042
  const [mode, setMode] = (0, import_react.useState)("chat");
2101
2043
  const [busy, setBusy] = (0, import_react.useState)(false);
2102
2044
  const [modelOpen, setModelOpen] = (0, import_react.useState)(false);
2103
2045
  const [permOpen, setPermOpen] = (0, import_react.useState)(false);
2104
2046
  const [peerOpen, setPeerOpen] = (0, import_react.useState)(false);
2105
- const [mentionActive, setMentionActive2] = (0, import_react.useState)(0);
2106
- const [caret, setCaret2] = (0, import_react.useState)(0);
2047
+ const [mentionActive, setMentionActive] = (0, import_react.useState)(0);
2048
+ const [caret, setCaret] = (0, import_react.useState)(0);
2107
2049
  const [mentionDismissed, setMentionDismissed] = (0, import_react.useState)(false);
2108
- const taRef2 = (0, import_react.useRef)(null);
2050
+ const taRef = (0, import_react.useRef)(null);
2109
2051
  const offline = room.status !== "online";
2110
- const mqRaw = mentionQueryAt(draft2, caret);
2111
- const mq2 = mentionDismissed ? null : mqRaw;
2052
+ const mqRaw = mentionQueryAt(draft, caret);
2053
+ const mq = mentionDismissed ? null : mqRaw;
2112
2054
  const mentionItems = (() => {
2113
- if (!mq2) return [];
2114
- const q = mq2.query.toLowerCase();
2055
+ if (!mq) return [];
2056
+ const q = mq.query.toLowerCase();
2115
2057
  const members = (room.members || []).filter((m) => !m.self);
2116
2058
  const known = members.map((m) => ({
2117
2059
  id: m.agentId,
@@ -2135,7 +2077,7 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
2135
2077
  return bits.length ? `Auto \xB7 ${bits.join("+")}` : "Ask to run";
2136
2078
  })();
2137
2079
  (0, import_react.useEffect)(() => {
2138
- setMentionActive2(0);
2080
+ setMentionActive(0);
2139
2081
  setMentionDismissed(false);
2140
2082
  }, [mqRaw && mqRaw.query, mqRaw && mqRaw.start]);
2141
2083
  const MODELS = ["deepseek-chat", "deepseek-reasoner", "deepseek-coder"];
@@ -2158,28 +2100,8 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
2158
2100
  a.toast(e.message);
2159
2101
  }
2160
2102
  };
2161
- const syncCaret = (el) => {
2162
- if (!el) return;
2163
- setCaret2(el.selectionStart ?? el.value.length);
2164
- };
2165
- const insertMention = (it) => {
2166
- if (!mq2) return;
2167
- const insert = `@${it.id} `;
2168
- const next = draft2.slice(0, mq2.start) + insert + draft2.slice(mq2.end);
2169
- setDraft2(next);
2170
- const pos = mq2.start + insert.length;
2171
- setCaret2(pos);
2172
- setMentionActive2(0);
2173
- requestAnimationFrame(() => {
2174
- const el = taRef2.current;
2175
- if (el) {
2176
- el.focus();
2177
- el.setSelectionRange(pos, pos);
2178
- }
2179
- });
2180
- };
2181
2103
  const submit = async () => {
2182
- const text = draft2.trim();
2104
+ const text = draft.trim();
2183
2105
  if (!text || busy) return;
2184
2106
  setBusy(true);
2185
2107
  try {
@@ -2188,7 +2110,7 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
2188
2110
  await a.task(room.roomId, m ? text.slice(m[0].length) : text, m ? m[1] : void 0);
2189
2111
  setMode("chat");
2190
2112
  } else await a.send(room.roomId, text);
2191
- setDraft2("");
2113
+ setDraft("");
2192
2114
  } catch (e) {
2193
2115
  a.toast(e.message);
2194
2116
  } finally {
@@ -2197,33 +2119,33 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
2197
2119
  };
2198
2120
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-composer-card", "data-rm-composer": "", children: [
2199
2121
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-composer-grow", children: [
2200
- mq2 && mentionItems.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MentionMenu, { items: mentionItems, active: mentionActive, onHover: setMentionActive2, onPick: insertMention }) : null,
2122
+ mq && mentionItems.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MentionMenu, { items: mentionItems, active: mentionActive, onHover: setMentionActive, onPick: insertMention }) : null,
2201
2123
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2202
2124
  "textarea",
2203
2125
  {
2204
- ref: taRef2,
2126
+ ref: taRef,
2205
2127
  rows: 1,
2206
2128
  disabled: offline,
2207
2129
  "aria-label": "Message the room",
2208
2130
  placeholder: offline ? "Room is offline" : mode === "task" ? "Task: @assignee title (optional assignee)" : `Message ${room.name} \xB7 @agent or @all`,
2209
- value: draft2,
2131
+ value: draft,
2210
2132
  onChange: (e) => {
2211
- setDraft2(e.target.value);
2133
+ setDraft(e.target.value);
2212
2134
  syncCaret(e.target);
2213
2135
  },
2214
2136
  onClick: (e) => syncCaret(e.target),
2215
2137
  onKeyUp: (e) => syncCaret(e.target),
2216
2138
  onSelect: (e) => syncCaret(e.target),
2217
2139
  onKeyDown: (e) => {
2218
- if (mq2 && mentionItems.length) {
2140
+ if (mq && mentionItems.length) {
2219
2141
  if (e.key === "ArrowDown") {
2220
2142
  e.preventDefault();
2221
- setMentionActive2((i) => (i + 1) % mentionItems.length);
2143
+ setMentionActive((i) => (i + 1) % mentionItems.length);
2222
2144
  return;
2223
2145
  }
2224
2146
  if (e.key === "ArrowUp") {
2225
2147
  e.preventDefault();
2226
- setMentionActive2((i) => (i - 1 + mentionItems.length) % mentionItems.length);
2148
+ setMentionActive((i) => (i - 1 + mentionItems.length) % mentionItems.length);
2227
2149
  return;
2228
2150
  }
2229
2151
  if (e.key === "Enter" || e.key === "Tab") {
@@ -2309,7 +2231,7 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
2309
2231
  peerOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PeerGrantsMenu, { room, store, onClose: () => setPeerOpen(false), patchConfig }) : null
2310
2232
  ] })
2311
2233
  ] }),
2312
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-composer-trailing", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-composer-send", "aria-label": mode === "task" ? "Create task" : "Send", disabled: !draft2.trim() || busy || offline, onClick: submit, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SendIcon, {}) }) })
2234
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-composer-trailing", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-composer-send", "aria-label": mode === "task" ? "Create task" : "Send", disabled: !draft.trim() || busy || offline, onClick: submit, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SendIcon, {}) }) })
2313
2235
  ] })
2314
2236
  ] });
2315
2237
  }
@@ -2446,39 +2368,33 @@ function RoomDetail({ room, messages, store, mention, assign }) {
2446
2368
  ] });
2447
2369
  }
2448
2370
  function EmptyMain({ store }) {
2449
- const s = useStore(store);
2450
2371
  const a = store.actions;
2451
- 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: [
2452
2375
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-badge", children: "Collab \xB7 E2EE \xB7 no accounts" }),
2453
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: "Workspaces, then rooms" }),
2454
- /* @__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." }),
2455
2378
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-welcome-actions", children: [
2456
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "workspace" }), children: [
2457
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
2458
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
2459
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Bind workspace" }),
2460
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Local project path shared by agents in rooms" })
2461
- ] })
2462
- ] }),
2463
- /* @__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: [
2464
2380
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}),
2465
2381
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
2466
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Create room" }),
2467
- /* @__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" })
2468
2384
  ] })
2469
2385
  ] }),
2470
2386
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "join" }), children: [
2471
2387
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkIcon, {}),
2472
2388
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
2473
2389
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Join by invite" }),
2474
- /* @__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" })
2475
2391
  ] })
2476
2392
  ] }),
2477
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.setSearchOpen(true), children: [
2393
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "browse" }), children: [
2478
2394
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
2479
2395
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
2480
2396
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Browse public rooms" }),
2481
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Search the relay directory by name or #tag" })
2397
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Search the VPS directory by name or #tag" })
2482
2398
  ] })
2483
2399
  ] })
2484
2400
  ] })
@@ -2488,51 +2404,60 @@ function RoomsMain({ store }) {
2488
2404
  const s = useStore(store);
2489
2405
  const [drafts, setDrafts] = (0, import_react.useState)({});
2490
2406
  const room = s.rooms.find((r) => r.roomId === s.selected);
2491
- 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
+ ] });
2492
2414
  const messages = s.messages[room.roomId] || [];
2493
- const draft2 = drafts[room.roomId] || "";
2494
- const setDraft2 = (v) => setDrafts((d) => ({ ...d, [room.roomId]: typeof v === "function" ? v(d[room.roomId] || "") : v }));
2415
+ const draft = drafts[room.roomId] || "";
2416
+ const setDraft = (v) => setDrafts((d) => ({ ...d, [room.roomId]: typeof v === "function" ? v(d[room.roomId] || "") : v }));
2495
2417
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-main", "data-collab-main": "", children: [
2496
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApprovalBanner, { store }),
2497
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-workspace", children: [
2498
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "rm-chat", children: [
2499
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "rm-chat-header", children: [
2500
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: room.name, size: 28 }),
2501
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
2502
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-chat-header-title", children: room.name }),
2503
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-chat-header-sub", children: [
2504
- room.members.filter((m) => m.online !== false).length,
2505
- " online \xB7 ",
2506
- room.public ? "public" : "private",
2507
- room.status !== "online" ? ` \xB7 room ${room.status}` : ""
2508
- ] })
2509
- ] }),
2510
- room.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge rm-badge-public", children: [
2511
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
2512
- "Public"
2513
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge", children: [
2514
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}),
2515
- "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 })
2516
2443
  ] }),
2517
- room.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
2518
- /* @__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 })
2519
2446
  ] }),
2520
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages }),
2521
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Composer, { room, store, draft: draft2, setDraft: setDraft2 })
2522
- ] }),
2523
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2524
- RoomDetail,
2525
- {
2526
- room,
2527
- messages,
2528
- store,
2529
- mention: (id) => setDraft2((d) => `${d}${d && !d.endsWith(" ") ? " " : ""}@${id} `),
2530
- assign: (id) => {
2531
- setDraft2(`@${id} `);
2532
- 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
+ }
2533
2458
  }
2534
- }
2535
- )
2459
+ )
2460
+ ] })
2536
2461
  ] })
2537
2462
  ] });
2538
2463
  }
@@ -2543,7 +2468,7 @@ function Field({ label, hint, children, htmlFor }) {
2543
2468
  hint ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: hint }) : null
2544
2469
  ] });
2545
2470
  }
2546
- function CreateDialog({ store }) {
2471
+ function CreateDialog({ store, d }) {
2547
2472
  const s = useStore(store);
2548
2473
  const a = store.actions;
2549
2474
  const [f, setF] = (0, import_react.useState)({ name: "", description: "", tags: "", public: false, mode: "lan", lanHostPort: "62.234.113.56:80" });
@@ -2551,28 +2476,13 @@ function CreateDialog({ store }) {
2551
2476
  const [busy, setBusy] = (0, import_react.useState)(false);
2552
2477
  const [lanPeers, setLanPeers] = (0, import_react.useState)([]);
2553
2478
  const ok = f.name.trim() && !busy;
2554
- const syncCaret = (el) => {
2555
- if (!el) return;
2556
- setCaret(el.selectionStart ?? el.value.length);
2557
- };
2558
- const insertMention = (it) => {
2559
- if (!mq) return;
2560
- const insert = `@${it.id} `;
2561
- const next = draft.slice(0, mq.start) + insert + draft.slice(mq.end);
2562
- setDraft(next);
2563
- const pos = mq.start + insert.length;
2564
- setCaret(pos);
2565
- setMentionActive(0);
2566
- requestAnimationFrame(() => {
2567
- const el = taRef.current;
2568
- if (el) {
2569
- el.focus();
2570
- el.setSelectionRange(pos, pos);
2571
- }
2572
- });
2573
- };
2479
+ const [ws, setWs] = (0, import_react.useState)(d && d.workspace ? d.workspace : null);
2574
2480
  const submit = async () => {
2575
2481
  if (!ok) return;
2482
+ if (!ws) {
2483
+ setErr("Pick a workspace");
2484
+ return;
2485
+ }
2576
2486
  setBusy(true);
2577
2487
  setErr(null);
2578
2488
  try {
@@ -2582,7 +2492,9 @@ function CreateDialog({ store }) {
2582
2492
  tags: f.tags,
2583
2493
  public: f.public,
2584
2494
  mode: f.mode,
2585
- 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
2586
2498
  });
2587
2499
  } catch (e) {
2588
2500
  setErr(e.message);
@@ -2604,23 +2516,25 @@ function CreateDialog({ store }) {
2604
2516
  onClose: a.closeDialog,
2605
2517
  title: "Create room",
2606
2518
  closeLabel: "Close",
2607
- description: "Room is created inside the active workspace (path becomes MCP sandbox). Default transport is the VPS Node relay (62.234.113.56:80) with optional public directory. Cloudflare remains an advanced option. The room key never goes to the relay.",
2519
+ className: "rm-modal-wide",
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.",
2608
2521
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2609
2522
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
2610
2523
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: !ok, onClick: submit, children: busy ? "Creating\u2026" : "Create" })
2611
2524
  ] }),
2612
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form", onKeyDown: (e) => e.key === "Enter" && e.target.tagName === "INPUT" && submit(), children: [
2613
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Transport", hint: "Default: VPS Node relay at 62.234.113.56:80 (invite-only or public directory). Blank host = embedded local LAN + mDNS. Cloudflare is optional.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
2614
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.mode === "lan" ? " is-on" : ""), onClick: () => setF({ ...f, mode: "lan", lanHostPort: f.lanHostPort || "62.234.113.56:80" }), children: "LAN / VPS" }),
2615
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.mode === "relay" ? " is-on" : ""), onClick: () => setF({ ...f, mode: "relay" }), children: "Cloudflare (advanced)" })
2616
- ] }) }),
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 }) }),
2617
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: [
2618
2528
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: f.name || "room", size: 36 }),
2619
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 }) })
2620
2530
  ] }) }),
2621
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Description (optional)", htmlFor: "rm-create-desc", hint: "Shown to members and, if public, in search results.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-desc", placeholder: "e.g. Agents that review and ship releases", maxLength: 280, value: f.description, onChange: (e) => setF({ ...f, description: e.target.value }) }) }),
2622
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Tags (optional)", htmlFor: "rm-create-tags", hint: "Spaces or commas, up to 8. Used by public room search.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-tags", placeholder: "e.g. review rust", value: f.tags, onChange: (e) => setF({ ...f, tags: e.target.value }) }) }),
2623
- f.mode === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, { label: "Relay host:port", htmlFor: "rm-lan-hp", hint: "Default VPS 62.234.113.56:80 (public directory works). Blank = this machine (LAN/Tailscale + mDNS). Invite: collab://lan/host:port/room#key.", children: [
2531
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Transport", hint: "VPS directory works on LAN/VPS. Cloudflare is optional.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
2532
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.mode === "lan" ? " is-on" : ""), onClick: () => setF({ ...f, mode: "lan", lanHostPort: f.lanHostPort || "62.234.113.56:80" }), children: "LAN / VPS" }),
2533
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.mode === "relay" ? " is-on" : ""), onClick: () => setF({ ...f, mode: "relay" }), children: "Cloudflare" })
2534
+ ] }) }),
2535
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Description", htmlFor: "rm-create-desc", hint: "Shown to members and in public search.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-desc", placeholder: "e.g. Agents that review and ship releases", maxLength: 280, value: f.description, onChange: (e) => setF({ ...f, description: e.target.value }) }) }),
2536
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Tags", htmlFor: "rm-create-tags", hint: "Spaces or commas, up to 8.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-tags", placeholder: "e.g. review rust", value: f.tags, onChange: (e) => setF({ ...f, tags: e.target.value }) }) }),
2537
+ f.mode === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, { label: "Relay host:port", htmlFor: "rm-lan-hp", hint: "Default 62.234.113.56:80. Blank = this machine + mDNS.", children: [
2624
2538
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-lan-hp", placeholder: "62.234.113.56:80 or 100.x.x.x:7421", value: f.lanHostPort, onChange: (e) => setF({ ...f, lanHostPort: e.target.value }) }),
2625
2539
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-lan-browse", children: [
2626
2540
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: browseLan, children: "Browse mDNS" }),
@@ -2629,15 +2543,15 @@ function CreateDialog({ store }) {
2629
2543
  p.name ? ` \xB7 ${p.name}` : ""
2630
2544
  ] }, p.hostPort))
2631
2545
  ] })
2632
- ] }) : null,
2633
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
2546
+ ] }) }) : null,
2547
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-switch-row", children: [
2634
2548
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
2635
2549
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "List in public directory" }),
2636
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: f.public ? "Anyone can find this room by name or tag and join it. Its key is published with the listing \u2014 NOT confidential." : "Private: invite only. Nothing about the room is published." })
2550
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: f.public ? "Anyone can find and join \u2014 key is published with the listing." : "Private: invite only." })
2637
2551
  ] }),
2638
2552
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.public, onChange: (v) => setF({ ...f, public: v }), label: "List in public directory" })
2639
2553
  ] }),
2640
- err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: err }) : null
2554
+ err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-error", children: err }) : null
2641
2555
  ] })
2642
2556
  }
2643
2557
  );
@@ -2649,32 +2563,13 @@ function JoinDialog({ store }) {
2649
2563
  const [err, setErr] = (0, import_react.useState)(null);
2650
2564
  const [busy, setBusy] = (0, import_react.useState)(false);
2651
2565
  const valid = /^collab:\/\/(join|lan|relay)\/.+#\S+$/.test(invite.trim());
2652
- const syncCaret = (el) => {
2653
- if (!el) return;
2654
- setCaret(el.selectionStart ?? el.value.length);
2655
- };
2656
- const insertMention = (it) => {
2657
- if (!mq) return;
2658
- const insert = `@${it.id} `;
2659
- const next = draft.slice(0, mq.start) + insert + draft.slice(mq.end);
2660
- setDraft(next);
2661
- const pos = mq.start + insert.length;
2662
- setCaret(pos);
2663
- setMentionActive(0);
2664
- requestAnimationFrame(() => {
2665
- const el = taRef.current;
2666
- if (el) {
2667
- el.focus();
2668
- el.setSelectionRange(pos, pos);
2669
- }
2670
- });
2671
- };
2566
+ const [ws, setWs] = (0, import_react.useState)(null);
2672
2567
  const submit = async () => {
2673
2568
  if (!valid || busy) return;
2674
2569
  setBusy(true);
2675
2570
  setErr(null);
2676
2571
  try {
2677
- await a.join(invite.trim());
2572
+ await a.join(invite.trim(), ws ? { workspacePath: ws.path, workspaceName: ws.title } : void 0);
2678
2573
  } catch (e) {
2679
2574
  setErr(e.message);
2680
2575
  setBusy(false);
@@ -2687,12 +2582,13 @@ function JoinDialog({ store }) {
2687
2582
  onClose: a.closeDialog,
2688
2583
  title: "Join room",
2689
2584
  closeLabel: "Close",
2690
- 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.",
2691
2586
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2692
2587
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
2693
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" })
2694
2589
  ] }),
2695
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 }),
2696
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() }),
2697
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,
2698
2594
  err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: err }) : null
@@ -2773,26 +2669,6 @@ function ConfigDialog({ store, d }) {
2773
2669
  a.getConfig(d.roomId).then((c) => setF((x) => ({ ...x, workspacePath: c.workspacePath || "", workspaceLabel: c.workspaceLabel || x.workspaceLabel, model: c.model || "", provider: c.provider || "", reasoningEffort: c.reasoningEffort || "medium", autoRead: c.permissions?.autoRead !== false, autoWrite: !!c.permissions?.autoWrite, autoShell: !!c.permissions?.autoShell, autoNetwork: !!c.permissions?.autoNetwork, mcpEnabled: c.mcpEnabled !== false }))).catch(() => {
2774
2670
  });
2775
2671
  }, [d.roomId]);
2776
- const syncCaret = (el) => {
2777
- if (!el) return;
2778
- setCaret(el.selectionStart ?? el.value.length);
2779
- };
2780
- const insertMention = (it) => {
2781
- if (!mq) return;
2782
- const insert = `@${it.id} `;
2783
- const next = draft.slice(0, mq.start) + insert + draft.slice(mq.end);
2784
- setDraft(next);
2785
- const pos = mq.start + insert.length;
2786
- setCaret(pos);
2787
- setMentionActive(0);
2788
- requestAnimationFrame(() => {
2789
- const el = taRef.current;
2790
- if (el) {
2791
- el.focus();
2792
- el.setSelectionRange(pos, pos);
2793
- }
2794
- });
2795
- };
2796
2672
  const submit = async () => {
2797
2673
  setBusy(true);
2798
2674
  setErr(null);
@@ -2894,26 +2770,6 @@ function WorkspaceDialog({ store }) {
2894
2770
  const [path, setPath] = (0, import_react.useState)("");
2895
2771
  const [err, setErr] = (0, import_react.useState)(null);
2896
2772
  const [busy, setBusy] = (0, import_react.useState)(false);
2897
- const syncCaret = (el) => {
2898
- if (!el) return;
2899
- setCaret(el.selectionStart ?? el.value.length);
2900
- };
2901
- const insertMention = (it) => {
2902
- if (!mq) return;
2903
- const insert = `@${it.id} `;
2904
- const next = draft.slice(0, mq.start) + insert + draft.slice(mq.end);
2905
- setDraft(next);
2906
- const pos = mq.start + insert.length;
2907
- setCaret(pos);
2908
- setMentionActive(0);
2909
- requestAnimationFrame(() => {
2910
- const el = taRef.current;
2911
- if (el) {
2912
- el.focus();
2913
- el.setSelectionRange(pos, pos);
2914
- }
2915
- });
2916
- };
2917
2773
  const submit = async () => {
2918
2774
  if (!path.trim()) {
2919
2775
  setErr("Absolute workspace path required");
@@ -2922,9 +2778,10 @@ function WorkspaceDialog({ store }) {
2922
2778
  setBusy(true);
2923
2779
  setErr(null);
2924
2780
  try {
2925
- 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() });
2926
2783
  a.closeDialog();
2927
- 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");
2928
2785
  } catch (e) {
2929
2786
  setErr(e.message);
2930
2787
  setBusy(false);
@@ -2935,18 +2792,14 @@ function WorkspaceDialog({ store }) {
2935
2792
  {
2936
2793
  open: true,
2937
2794
  onClose: a.closeDialog,
2938
- title: "Workspace",
2795
+ title: "Add workspace",
2939
2796
  closeLabel: "Close",
2940
- 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.",
2941
2798
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2942
2799
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
2943
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" })
2944
2801
  ] }),
2945
2802
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form", children: [
2946
- (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 () => {
2947
- await a.setActiveWorkspace(w.id);
2948
- a.closeDialog();
2949
- }, children: w.name }, w.id)) }) }) : null,
2950
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) }) }),
2951
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() }) }),
2952
2805
  err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: err }) : null
@@ -2954,16 +2807,90 @@ function WorkspaceDialog({ store }) {
2954
2807
  }
2955
2808
  );
2956
2809
  }
2810
+ function BrowseDialog({ store }) {
2811
+ const s = useStore(store);
2812
+ const a = store.actions;
2813
+ const [joining, setJoining] = (0, import_react.useState)(null);
2814
+ const inputRef = (0, import_react.useRef)(null);
2815
+ const [activeWs, setActiveWs] = (0, import_react.useState)(null);
2816
+ (0, import_react.useEffect)(() => {
2817
+ a.setSearchOpen(true);
2818
+ requestAnimationFrame(() => inputRef.current?.focus());
2819
+ return () => {
2820
+ };
2821
+ }, []);
2822
+ const join = async (r) => {
2823
+ setJoining(r.roomId);
2824
+ try {
2825
+ await a.join(r.invite, { name: r.name, description: r.description, tags: r.tags, public: true, workspacePath: activeWs?.path, workspaceName: activeWs?.title });
2826
+ } catch (e) {
2827
+ a.toast(e.message);
2828
+ } finally {
2829
+ setJoining(null);
2830
+ }
2831
+ };
2832
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2833
+ import_dsh_client_ui_primitives.Modal,
2834
+ {
2835
+ open: true,
2836
+ onClose: a.closeDialog,
2837
+ title: "Browse public rooms",
2838
+ closeLabel: "Close",
2839
+ className: "rm-modal-wide",
2840
+ description: "Directory on VPS 62.234.113.56:80. Search by name or #tag; joined rooms attach to the workspace below.",
2841
+ footer: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Close" }),
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 }),
2844
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search rm-browse-search", children: [
2845
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
2846
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2847
+ "input",
2848
+ {
2849
+ ref: inputRef,
2850
+ type: "text",
2851
+ placeholder: "Search rooms or #tags",
2852
+ value: s.search.query,
2853
+ onChange: (e) => a.setQuery(e.target.value),
2854
+ "aria-label": "Search public rooms"
2855
+ }
2856
+ ),
2857
+ s.search.loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-browse-status", children: "Searching\u2026" }) : null
2858
+ ] }),
2859
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-browse-list", children: [
2860
+ s.search.results.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-dir-item", "data-dir-room-id": r.roomId, children: [
2861
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: r.name, size: 30 }),
2862
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-dir-item-body", children: [
2863
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-room-item-name", children: r.name }),
2864
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-room-item-meta", children: [
2865
+ r.description || "No description",
2866
+ " \xB7 ",
2867
+ r.members,
2868
+ " online"
2869
+ ] }),
2870
+ (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: [
2871
+ "#",
2872
+ tg
2873
+ ] }, tg)) }) : null
2874
+ ] }),
2875
+ /* @__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 || !activeWs, onClick: () => join(r), children: !activeWs ? "Need workspace" : r.joined ? "Joined" : joining === r.roomId ? "\u2026" : "Join" })
2876
+ ] }, r.roomId)),
2877
+ !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 listed yet. Create a room with \u201CList in public directory\u201D on, then refresh here." }) : null
2878
+ ] })
2879
+ ] })
2880
+ }
2881
+ );
2882
+ }
2957
2883
  function Overlay({ store }) {
2958
2884
  const s = useStore(store);
2959
2885
  const d = s.dialog;
2960
2886
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2961
- 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,
2962
2888
  d?.kind === "join" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JoinDialog, { store }) : null,
2963
2889
  d?.kind === "invite" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InviteDialog, { store, d }) : null,
2964
2890
  d?.kind === "leave" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LeaveDialog, { store, d }) : null,
2965
2891
  d?.kind === "config" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConfigDialog, { store, d }) : null,
2966
2892
  d?.kind === "workspace" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceDialog, { store }) : null,
2893
+ d?.kind === "browse" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BrowseDialog, { store }) : null,
2967
2894
  s.toast ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Toast, { text: s.toast, onDone: store.actions.clearToast, holdMs: 2200 }) : null
2968
2895
  ] });
2969
2896
  }
@@ -2976,8 +2903,118 @@ function installStyles() {
2976
2903
  document.head.appendChild(style);
2977
2904
  return () => style.remove();
2978
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
+ }
2979
3005
  function apply(ctx) {
2980
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;
2981
3018
  ctx.effect(() => installStyles(), "dsh-room: styles");
2982
3019
  ctx.effect(() => () => store.dispose(), "dsh-room: store");
2983
3020
  const info = () => ctx.layout.panelInfo;
@@ -2987,27 +3024,12 @@ function apply(ctx) {
2987
3024
  } catch {
2988
3025
  }
2989
3026
  };
3027
+ store.onRoomOpened = ensurePanel;
2990
3028
  ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({ name: "sidebar.panellist", id: PANEL_ID, order: 11, label: () => "DSH Room" }, PanelIcon));
2991
3029
  ctx.slots.inject("main", () => ctx.slots.register({ name: "main", key: PANEL_ID, inject: () => ({ store }) }, RoomsMain));
2992
3030
  ctx.slots.inject("shell.overlay", () => ctx.slots.register({ name: "shell.overlay", id: "dsh-room-dialogs", inject: () => ({ store }) }, Overlay));
2993
- ctx.slots.inject("sidebar.workspaces", () => {
2994
- let dispose;
2995
- const reconcile = () => {
2996
- const active = info().getSnapshot().activePanelId === PANEL_ID;
2997
- if (active && !dispose) dispose = ctx.slots.register({ name: "sidebar.workspaces", priority: -100, inject: () => ({ store, ensurePanel }) }, RoomsSidebar);
2998
- else if (!active && dispose) {
2999
- dispose();
3000
- dispose = void 0;
3001
- }
3002
- };
3003
- const off = info().subscribe(reconcile);
3004
- reconcile();
3005
- return () => {
3006
- off();
3007
- if (dispose) dispose();
3008
- dispose = void 0;
3009
- };
3010
- });
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");
3011
3033
  }
3012
3034
 
3013
3035
  return module.exports;