@cecwxf/dsh-room 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/client.js +268 -300
  2. package/package.json +1 -1
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';
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
  }
@@ -291,9 +297,9 @@ function RoomsSidebar({ store, ensurePanel }) {
291
297
  const s = useStore(store);
292
298
  const a = store.actions;
293
299
  const [menu, setMenu] = (0, import_react.useState)(null);
294
- const [searching, setSearching] = (0, import_react.useState)(false);
295
300
  const [joining, setJoining] = (0, import_react.useState)(null);
296
301
  const inputRef = (0, import_react.useRef)(null);
302
+ const searching = !!s.search.open;
297
303
  const activeWs = (s.workspaces || []).find((w) => w.id === s.activeWorkspaceId) || (s.workspaces || [])[0] || null;
298
304
  (0, import_react.useEffect)(() => {
299
305
  if (!menu) return;
@@ -333,10 +339,7 @@ function RoomsSidebar({ store, ensurePanel }) {
333
339
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-side", "data-collab-sidebar": "", children: [
334
340
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-side-toolbar", children: [
335
341
  /* @__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, {}) }),
342
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-icon-btn", "aria-label": searching ? "Close search" : "Search rooms", "aria-pressed": searching, onClick: () => a.setSearchOpen(!searching), children: searching ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CloseIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}) }),
340
343
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
341
344
  "button",
342
345
  {
@@ -378,8 +381,7 @@ function RoomsSidebar({ store, ensurePanel }) {
378
381
  ] }),
379
382
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
380
383
  setMenu(null);
381
- setSearching(true);
382
- a.setSearchOpen(true);
384
+ a.openDialog({ kind: "browse" });
383
385
  }, children: [
384
386
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
385
387
  "Browse public rooms"
@@ -391,7 +393,14 @@ function RoomsSidebar({ store, ensurePanel }) {
391
393
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
392
394
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: activeWs.name })
393
395
  ] }),
394
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-path", title: activeWs.path, children: activeWs.path })
396
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-path", title: activeWs.path, children: activeWs.path }),
397
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-new-room", "data-rm-new-room": "", onClick: () => {
398
+ a.openDialog({ kind: "create" });
399
+ ensurePanel();
400
+ }, children: [
401
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}),
402
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "New Room" })
403
+ ] })
395
404
  ] }),
396
405
  searching ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search", children: [
397
406
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
@@ -467,7 +476,7 @@ function RoomsSidebar({ store, ensurePanel }) {
467
476
  r.members,
468
477
  " online"
469
478
  ] }),
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: [
479
+ (r.tags || []).length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-dir-tags", children: (r.tags || []).map((tg) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-tag", children: [
471
480
  "#",
472
481
  tg
473
482
  ] }, tg)) }) : null
@@ -588,23 +597,23 @@ function MentionMenu({ items, active, onPick, onHover }) {
588
597
  it.id
589
598
  )) });
590
599
  }
591
- function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
600
+ function Composer({ room, store, draft, setDraft }) {
592
601
  const a = store.actions;
593
602
  const [mode, setMode] = (0, import_react.useState)("chat");
594
603
  const [busy, setBusy] = (0, import_react.useState)(false);
595
604
  const [modelOpen, setModelOpen] = (0, import_react.useState)(false);
596
605
  const [permOpen, setPermOpen] = (0, import_react.useState)(false);
597
606
  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);
607
+ const [mentionActive, setMentionActive] = (0, import_react.useState)(0);
608
+ const [caret, setCaret] = (0, import_react.useState)(0);
600
609
  const [mentionDismissed, setMentionDismissed] = (0, import_react.useState)(false);
601
- const taRef2 = (0, import_react.useRef)(null);
610
+ const taRef = (0, import_react.useRef)(null);
602
611
  const offline = room.status !== "online";
603
- const mqRaw = mentionQueryAt(draft2, caret);
604
- const mq2 = mentionDismissed ? null : mqRaw;
612
+ const mqRaw = mentionQueryAt(draft, caret);
613
+ const mq = mentionDismissed ? null : mqRaw;
605
614
  const mentionItems = (() => {
606
- if (!mq2) return [];
607
- const q = mq2.query.toLowerCase();
615
+ if (!mq) return [];
616
+ const q = mq.query.toLowerCase();
608
617
  const members = (room.members || []).filter((m) => !m.self);
609
618
  const known = members.map((m) => ({
610
619
  id: m.agentId,
@@ -628,7 +637,7 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
628
637
  return bits.length ? `Auto \xB7 ${bits.join("+")}` : "Ask to run";
629
638
  })();
630
639
  (0, import_react.useEffect)(() => {
631
- setMentionActive2(0);
640
+ setMentionActive(0);
632
641
  setMentionDismissed(false);
633
642
  }, [mqRaw && mqRaw.query, mqRaw && mqRaw.start]);
634
643
  const MODELS = ["deepseek-chat", "deepseek-reasoner", "deepseek-coder"];
@@ -651,28 +660,8 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
651
660
  a.toast(e.message);
652
661
  }
653
662
  };
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
663
  const submit = async () => {
675
- const text = draft2.trim();
664
+ const text = draft.trim();
676
665
  if (!text || busy) return;
677
666
  setBusy(true);
678
667
  try {
@@ -681,7 +670,7 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
681
670
  await a.task(room.roomId, m ? text.slice(m[0].length) : text, m ? m[1] : void 0);
682
671
  setMode("chat");
683
672
  } else await a.send(room.roomId, text);
684
- setDraft2("");
673
+ setDraft("");
685
674
  } catch (e) {
686
675
  a.toast(e.message);
687
676
  } finally {
@@ -690,33 +679,33 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
690
679
  };
691
680
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-composer-card", "data-rm-composer": "", children: [
692
681
  /* @__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,
682
+ mq && mentionItems.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MentionMenu, { items: mentionItems, active: mentionActive, onHover: setMentionActive, onPick: insertMention }) : null,
694
683
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
695
684
  "textarea",
696
685
  {
697
- ref: taRef2,
686
+ ref: taRef,
698
687
  rows: 1,
699
688
  disabled: offline,
700
689
  "aria-label": "Message the room",
701
690
  placeholder: offline ? "Room is offline" : mode === "task" ? "Task: @assignee title (optional assignee)" : `Message ${room.name} \xB7 @agent or @all`,
702
- value: draft2,
691
+ value: draft,
703
692
  onChange: (e) => {
704
- setDraft2(e.target.value);
693
+ setDraft(e.target.value);
705
694
  syncCaret(e.target);
706
695
  },
707
696
  onClick: (e) => syncCaret(e.target),
708
697
  onKeyUp: (e) => syncCaret(e.target),
709
698
  onSelect: (e) => syncCaret(e.target),
710
699
  onKeyDown: (e) => {
711
- if (mq2 && mentionItems.length) {
700
+ if (mq && mentionItems.length) {
712
701
  if (e.key === "ArrowDown") {
713
702
  e.preventDefault();
714
- setMentionActive2((i) => (i + 1) % mentionItems.length);
703
+ setMentionActive((i) => (i + 1) % mentionItems.length);
715
704
  return;
716
705
  }
717
706
  if (e.key === "ArrowUp") {
718
707
  e.preventDefault();
719
- setMentionActive2((i) => (i - 1 + mentionItems.length) % mentionItems.length);
708
+ setMentionActive((i) => (i - 1 + mentionItems.length) % mentionItems.length);
720
709
  return;
721
710
  }
722
711
  if (e.key === "Enter" || e.key === "Tab") {
@@ -802,7 +791,7 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
802
791
  peerOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PeerGrantsMenu, { room, store, onClose: () => setPeerOpen(false), patchConfig }) : null
803
792
  ] })
804
793
  ] }),
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, {}) }) })
794
+ /* @__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
795
  ] })
807
796
  ] });
808
797
  }
@@ -967,11 +956,11 @@ function EmptyMain({ store }) {
967
956
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Paste a collab://join/\u2026 invite" })
968
957
  ] })
969
958
  ] }),
970
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.setSearchOpen(true), children: [
959
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "browse" }), children: [
971
960
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
972
961
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
973
962
  /* @__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" })
963
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Search the VPS directory by name or #tag" })
975
964
  ] })
976
965
  ] })
977
966
  ] })
@@ -983,8 +972,8 @@ function RoomsMain({ store }) {
983
972
  const room = s.rooms.find((r) => r.roomId === s.selected);
984
973
  if (!room) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyMain, { store });
985
974
  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 }));
975
+ const draft = drafts[room.roomId] || "";
976
+ const setDraft = (v) => setDrafts((d) => ({ ...d, [room.roomId]: typeof v === "function" ? v(d[room.roomId] || "") : v }));
988
977
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-main", "data-collab-main": "", children: [
989
978
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApprovalBanner, { store }),
990
979
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-workspace", children: [
@@ -1011,7 +1000,7 @@ function RoomsMain({ store }) {
1011
1000
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: room.status === "online" ? "done" : room.status === "connecting" ? "ongoing" : "error", size: 8 })
1012
1001
  ] }),
1013
1002
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages }),
1014
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Composer, { room, store, draft: draft2, setDraft: setDraft2 })
1003
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Composer, { room, store, draft, setDraft })
1015
1004
  ] }),
1016
1005
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1017
1006
  RoomDetail,
@@ -1019,9 +1008,9 @@ function RoomsMain({ store }) {
1019
1008
  room,
1020
1009
  messages,
1021
1010
  store,
1022
- mention: (id) => setDraft2((d) => `${d}${d && !d.endsWith(" ") ? " " : ""}@${id} `),
1011
+ mention: (id) => setDraft((d) => `${d}${d && !d.endsWith(" ") ? " " : ""}@${id} `),
1023
1012
  assign: (id) => {
1024
- setDraft2(`@${id} `);
1013
+ setDraft(`@${id} `);
1025
1014
  store.actions.toast(`Switch to task mode, then describe the task for @${id}`);
1026
1015
  }
1027
1016
  }
@@ -1044,26 +1033,7 @@ function CreateDialog({ store }) {
1044
1033
  const [busy, setBusy] = (0, import_react.useState)(false);
1045
1034
  const [lanPeers, setLanPeers] = (0, import_react.useState)([]);
1046
1035
  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
- };
1036
+ const ws = (s.workspaces || []).find((w) => w.id === s.activeWorkspaceId) || (s.workspaces || [])[0];
1067
1037
  const submit = async () => {
1068
1038
  if (!ok) return;
1069
1039
  setBusy(true);
@@ -1097,23 +1067,24 @@ function CreateDialog({ store }) {
1097
1067
  onClose: a.closeDialog,
1098
1068
  title: "Create room",
1099
1069
  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.",
1070
+ className: "rm-modal-wide",
1071
+ description: ws ? `In workspace \u201C${ws.name}\u201D. Default relay 62.234.113.56:80 \xB7 E2EE \xB7 keys stay local.` : "Bind a workspace first. Default relay 62.234.113.56:80.",
1101
1072
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1102
1073
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
1103
1074
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: !ok, onClick: submit, children: busy ? "Creating\u2026" : "Create" })
1104
1075
  ] }),
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
- ] }) }),
1076
+ 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: [
1110
1077
  /* @__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
1078
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: f.name || "room", size: 36 }),
1112
1079
  /* @__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
1080
  ] }) }),
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: [
1081
+ /* @__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: [
1082
+ /* @__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" }),
1083
+ /* @__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" })
1084
+ ] }) }),
1085
+ /* @__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 }) }) }),
1086
+ /* @__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 }) }) }),
1087
+ 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
1088
  /* @__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
1089
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-lan-browse", children: [
1119
1090
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: browseLan, children: "Browse mDNS" }),
@@ -1122,15 +1093,15 @@ function CreateDialog({ store }) {
1122
1093
  p.name ? ` \xB7 ${p.name}` : ""
1123
1094
  ] }, p.hostPort))
1124
1095
  ] })
1125
- ] }) : null,
1126
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
1096
+ ] }) }) : null,
1097
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-switch-row", children: [
1127
1098
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1128
1099
  /* @__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." })
1100
+ /* @__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
1101
  ] }),
1131
1102
  /* @__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
1103
  ] }),
1133
- err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: err }) : null
1104
+ err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-error", children: err }) : null
1134
1105
  ] })
1135
1106
  }
1136
1107
  );
@@ -1142,26 +1113,6 @@ function JoinDialog({ store }) {
1142
1113
  const [err, setErr] = (0, import_react.useState)(null);
1143
1114
  const [busy, setBusy] = (0, import_react.useState)(false);
1144
1115
  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
- };
1165
1116
  const submit = async () => {
1166
1117
  if (!valid || busy) return;
1167
1118
  setBusy(true);
@@ -1266,26 +1217,6 @@ function ConfigDialog({ store, d }) {
1266
1217
  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
1218
  });
1268
1219
  }, [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
1220
  const submit = async () => {
1290
1221
  setBusy(true);
1291
1222
  setErr(null);
@@ -1387,26 +1318,6 @@ function WorkspaceDialog({ store }) {
1387
1318
  const [path, setPath] = (0, import_react.useState)("");
1388
1319
  const [err, setErr] = (0, import_react.useState)(null);
1389
1320
  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
1321
  const submit = async () => {
1411
1322
  if (!path.trim()) {
1412
1323
  setErr("Absolute workspace path required");
@@ -1447,6 +1358,78 @@ function WorkspaceDialog({ store }) {
1447
1358
  }
1448
1359
  );
1449
1360
  }
1361
+ function BrowseDialog({ store }) {
1362
+ const s = useStore(store);
1363
+ const a = store.actions;
1364
+ const [joining, setJoining] = (0, import_react.useState)(null);
1365
+ const inputRef = (0, import_react.useRef)(null);
1366
+ const activeWs = (s.workspaces || []).find((w) => w.id === s.activeWorkspaceId) || (s.workspaces || [])[0];
1367
+ (0, import_react.useEffect)(() => {
1368
+ a.setSearchOpen(true);
1369
+ requestAnimationFrame(() => inputRef.current?.focus());
1370
+ return () => {
1371
+ };
1372
+ }, []);
1373
+ const join = async (r) => {
1374
+ setJoining(r.roomId);
1375
+ try {
1376
+ await a.join(r.invite, { name: r.name, description: r.description, tags: r.tags, public: true, workspaceId: activeWs?.id });
1377
+ } catch (e) {
1378
+ a.toast(e.message);
1379
+ } finally {
1380
+ setJoining(null);
1381
+ }
1382
+ };
1383
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1384
+ import_dsh_client_ui_primitives.Modal,
1385
+ {
1386
+ open: true,
1387
+ onClose: a.closeDialog,
1388
+ title: "Browse public rooms",
1389
+ closeLabel: "Close",
1390
+ className: "rm-modal-wide",
1391
+ description: "Directory on VPS 62.234.113.56:80. Search by name or #tag, then join into the active workspace.",
1392
+ footer: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Close" }),
1393
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-browse", children: [
1394
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search rm-browse-search", children: [
1395
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
1396
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1397
+ "input",
1398
+ {
1399
+ ref: inputRef,
1400
+ type: "text",
1401
+ placeholder: "Search rooms or #tags",
1402
+ value: s.search.query,
1403
+ onChange: (e) => a.setQuery(e.target.value),
1404
+ "aria-label": "Search public rooms"
1405
+ }
1406
+ ),
1407
+ s.search.loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-browse-status", children: "Searching\u2026" }) : null
1408
+ ] }),
1409
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-browse-list", children: [
1410
+ s.search.results.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-dir-item", "data-dir-room-id": r.roomId, children: [
1411
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: r.name, size: 30 }),
1412
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-dir-item-body", children: [
1413
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-room-item-name", children: r.name }),
1414
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-room-item-meta", children: [
1415
+ r.description || "No description",
1416
+ " \xB7 ",
1417
+ r.members,
1418
+ " online"
1419
+ ] }),
1420
+ (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: [
1421
+ "#",
1422
+ tg
1423
+ ] }, tg)) }) : null
1424
+ ] }),
1425
+ /* @__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" })
1426
+ ] }, r.roomId)),
1427
+ !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
1428
+ ] })
1429
+ ] })
1430
+ }
1431
+ );
1432
+ }
1450
1433
  function Overlay({ store }) {
1451
1434
  const s = useStore(store);
1452
1435
  const d = s.dialog;
@@ -1457,6 +1440,7 @@ function Overlay({ store }) {
1457
1440
  d?.kind === "leave" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LeaveDialog, { store, d }) : null,
1458
1441
  d?.kind === "config" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConfigDialog, { store, d }) : null,
1459
1442
  d?.kind === "workspace" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceDialog, { store }) : null,
1443
+ d?.kind === "browse" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BrowseDialog, { store }) : null,
1460
1444
  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
1445
  ] });
1462
1446
  }
@@ -1544,7 +1528,7 @@ var import_react = require("react");
1544
1528
  var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
1545
1529
 
1546
1530
  // 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';
1531
+ var rooms_default = '/* Collab Rooms UI \u2014 original surface inside the dsh shell. Uses only dsh --dsw-* tokens.\n Independent Rooms surface. Prefix: rm- (rooms). */\n\n.rm-root {\n --rm-accent: var(--dsw-alias-state-business-primary);\n --rm-hover: var(--dsw-alias-interactive-bg-hover);\n --rm-border: var(--dsw-alias-border-l2);\n --rm-bg: var(--dsw-alias-bg-base);\n --rm-panel: var(--dsw-alias-bg-module-platform);\n --rm-label: var(--dsw-alias-label-primary);\n --rm-muted: var(--dsw-alias-label-secondary);\n --rm-quiet: var(--dsw-alias-label-tertiary);\n font: 13px/1.5 var(--dsw-font-family);\n color: var(--rm-label);\n box-sizing: border-box;\n}\n.rm-root *, .rm-root *::before, .rm-root *::after { box-sizing: border-box; }\n.rm-root button, .rm-root input, .rm-root textarea { font: inherit; }\n\n/* \u2500\u2500 sidebar (joined rooms + public search) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-side {\n display: flex; flex-direction: column; height: 100%; min-height: 0;\n padding: 0 8px 8px 0; gap: 8px;\n}\n.rm-side-toolbar {\n display: flex; align-items: center; gap: 6px; flex: none; padding: 4px 0 2px;\n}\n.rm-side-title {\n flex: 1; font-size: 12px; font-weight: 600; letter-spacing: .04em;\n text-transform: uppercase; color: var(--rm-quiet);\n}\n.rm-icon-btn {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; padding: 0;\n}\n.rm-icon-btn:hover { background: var(--rm-hover); color: var(--rm-label); }\n.rm-search {\n display: flex; align-items: center; gap: 6px; height: 32px; padding: 0 10px;\n border: .5px solid var(--rm-border); border-radius: 8px; background: var(--rm-panel);\n}\n.rm-search input {\n flex: 1; min-width: 0; border: 0; background: transparent; outline: none; color: var(--rm-label); font-size: 13px;\n}\n.rm-search input::placeholder { color: var(--rm-quiet); }\n.rm-side-scroll { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 12px; }\n.rm-group-label { font-size: 11px; font-weight: 600; color: var(--rm-quiet); padding: 0 4px 4px; text-transform: uppercase; letter-spacing: .03em; }\n.rm-room-list { display: flex; flex-direction: column; gap: 2px; }\n.rm-room-item {\n display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px;\n border: 0; border-radius: 10px; background: transparent; color: inherit; cursor: pointer; text-align: left;\n}\n.rm-room-item:hover { background: var(--rm-hover); }\n.rm-room-item[aria-current="true"] { background: var(--rm-hover); }\n.rm-room-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }\n.rm-room-item-name { font-weight: 600; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-meta { font-size: 11.5px; color: var(--rm-quiet); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-trail { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }\n.rm-badge {\n display: inline-flex; align-items: center; gap: 3px; height: 18px; padding: 0 6px;\n border-radius: 999px; font-size: 10.5px; font-weight: 500; background: var(--rm-panel); color: var(--rm-muted);\n border: .5px solid var(--rm-border);\n}\n.rm-badge-public { color: var(--rm-accent); border-color: color-mix(in srgb, var(--rm-accent) 35%, transparent); background: color-mix(in srgb, var(--rm-accent) 8%, transparent); }\n.rm-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--rm-quiet); }\n.rm-dot-online { background: var(--dsw-alias-state-success-primary, #2bb673); }\n.rm-dot-connecting { background: var(--dsw-alias-state-warn-primary, #e9a23b); }\n.rm-dot-offline { background: var(--dsw-alias-state-error-primary, #e5484d); }\n.rm-empty-hint { padding: 8px 10px; font-size: 12px; color: var(--rm-quiet); line-height: 1.45; }\n.rm-dir-item {\n display: flex; align-items: flex-start; gap: 10px; padding: 10px; border-radius: 10px;\n border: .5px solid var(--rm-border); background: var(--rm-bg); margin-bottom: 6px;\n}\n.rm-dir-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }\n.rm-dir-tags { display: flex; flex-wrap: wrap; gap: 4px; }\n.rm-tag { font-size: 11px; color: var(--rm-muted); background: var(--rm-hover); border-radius: 4px; padding: 0 5px; }\n.rm-menu {\n position: fixed; z-index: 1000; min-width: 180px; padding: 4px;\n border-radius: 10px; background: var(--dsw-alias-bg-layer-1, var(--rm-bg));\n border: .5px solid var(--rm-border);\n box-shadow: 0 8px 24px color-mix(in srgb, var(--rm-label) 12%, transparent);\n display: flex; flex-direction: column;\n}\n.rm-menu button {\n display: flex; align-items: center; gap: 8px; height: 32px; padding: 0 10px;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-menu button:hover { background: var(--rm-hover); }\n\n/* \u2500\u2500 main: room workspace \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-main { display: flex; flex: 1; min-height: 0; height: 100%; width: 100%; background: var(--rm-bg); }\n.rm-workspace { display: flex; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat-header {\n flex: none; display: flex; align-items: center; gap: 10px; height: 48px;\n padding: 0 16px; border-bottom: .5px solid var(--rm-border);\n}\n.rm-chat-header-title { font-size: 14px; font-weight: 600; }\n.rm-chat-header-sub { font-size: 12px; color: var(--rm-quiet); }\n.rm-messages {\n flex: 1; min-height: 0; overflow: auto; padding: 16px 18px 8px;\n display: flex; flex-direction: column; gap: 2px;\n}\n.rm-day {\n align-self: center; margin: 10px 0 12px; padding: 2px 10px; border-radius: 999px;\n font-size: 11px; color: var(--rm-quiet); background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n}\n.rm-msg {\n display: grid; grid-template-columns: 32px 1fr; gap: 0 10px; padding: 4px 6px; border-radius: 8px;\n max-width: 720px;\n}\n.rm-msg:hover { background: color-mix(in srgb, var(--rm-hover) 60%, transparent); }\n.rm-msg-self { margin-left: auto; grid-template-columns: 1fr; max-width: 640px; }\n.rm-msg-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 1px; }\n.rm-msg-author { font-size: 12px; font-weight: 600; color: var(--rm-muted); }\n.rm-msg-time { font-size: 11px; color: var(--rm-quiet); }\n.rm-msg-body { font-size: 13.5px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; }\n.rm-msg-self .rm-msg-body {\n background: var(--dsw-alias-label-primary); color: var(--dsw-alias-label-primary-foreground);\n padding: 8px 12px; border-radius: 12px 12px 4px 12px; width: fit-content; margin-left: auto;\n}\n.rm-msg-event {\n grid-template-columns: 1fr; max-width: 560px; margin: 6px 0;\n padding: 8px 12px; border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-panel);\n}\n.rm-mention { color: var(--rm-accent); font-weight: 500; }\n.rm-msg-self .rm-mention { color: inherit; text-decoration: underline; text-underline-offset: 2px; }\n.rm-task-card { display: flex; flex-direction: column; gap: 4px; }\n.rm-task-head { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--rm-muted); }\n.rm-task-title { font-weight: 600; font-size: 13.5px; }\n.rm-composer {\n flex: none; display: flex; align-items: flex-end; gap: 8px; padding: 10px 16px 14px;\n border-top: .5px solid var(--rm-border);\n}\n.rm-composer-box {\n flex: 1; display: flex; align-items: flex-end; gap: 6px; min-height: 44px;\n padding: 8px 10px; border: .5px solid var(--rm-border); border-radius: 12px; background: var(--rm-panel);\n}\n.rm-composer-box:focus-within { border-color: color-mix(in srgb, var(--rm-accent) 50%, var(--rm-border)); }\n.rm-composer-box textarea {\n flex: 1; min-height: 28px; max-height: 120px; border: 0; background: transparent; outline: none;\n resize: none; color: var(--rm-label); line-height: 1.45;\n}\n.rm-composer-box textarea::placeholder { color: var(--rm-quiet); }\n.rm-composer-mode {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; flex: none;\n}\n.rm-composer-mode:hover, .rm-composer-mode[aria-pressed="true"] {\n color: var(--rm-accent); background: color-mix(in srgb, var(--rm-accent) 12%, transparent);\n}\n.rm-chat-hint { margin: 48px auto; text-align: center; color: var(--rm-quiet); font-size: 13px; max-width: 360px; line-height: 1.5; }\n\n/* \u2500\u2500 detail column (members / invite / about) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-detail {\n flex: 0 0 280px; width: 280px; display: flex; flex-direction: column; min-height: 0;\n border-left: .5px solid var(--rm-border); background: var(--rm-bg); overflow: auto;\n}\n.rm-detail-section { padding: 14px 14px 8px; border-bottom: .5px solid var(--rm-border); }\n.rm-detail-section:last-child { border-bottom: 0; }\n.rm-detail-h {\n display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600;\n text-transform: uppercase; letter-spacing: .04em; color: var(--rm-quiet); margin-bottom: 8px;\n}\n.rm-member {\n display: flex; align-items: center; gap: 8px; padding: 5px 4px; border-radius: 8px; min-height: 34px;\n}\n.rm-member:hover { background: var(--rm-hover); }\n.rm-member-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }\n.rm-member-actions { display: none; gap: 2px; }\n.rm-member:hover .rm-member-actions, .rm-member:focus-within .rm-member-actions { display: inline-flex; }\n.rm-mini {\n width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-muted); cursor: pointer; font-size: 12px;\n}\n.rm-mini:hover { background: var(--dsw-alias-interactive-bg-active); color: var(--rm-label); }\n.rm-task-row { display: flex; align-items: center; gap: 6px; padding: 4px; font-size: 12.5px; }\n.rm-task-row-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-about { display: grid; grid-template-columns: 88px 1fr; gap: 6px 8px; font-size: 12.5px; margin: 0; }\n.rm-about dt { color: var(--rm-quiet); margin: 0; }\n.rm-about dd { margin: 0; min-width: 0; overflow-wrap: anywhere; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }\n.rm-mono { font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace); font-size: 11.5px; }\n.rm-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }\n.rm-danger { color: var(--dsw-alias-state-error-primary, #e5484d) !important; }\n.rm-note { font-size: 12px; color: var(--rm-quiet); line-height: 1.45; margin: 0 0 8px; }\n\n/* \u2500\u2500 empty / welcome \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-welcome-wrap {\n flex: 1; display: flex; align-items: center; justify-content: center; padding: 32px; background: var(--rm-bg);\n}\n.rm-welcome {\n width: min(420px, 100%); display: flex; flex-direction: column; gap: 10px;\n padding: 20px; border-radius: 14px; border: .5px solid var(--rm-border); background: var(--rm-panel);\n}\n.rm-welcome h1 { margin: 0; font-size: 16px; font-weight: 600; }\n.rm-welcome p { margin: 0; font-size: 13px; color: var(--rm-muted); line-height: 1.5; }\n.rm-welcome-actions { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }\n.rm-welcome-action {\n display: flex; align-items: flex-start; gap: 10px; width: 100%; padding: 10px 12px;\n border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-bg);\n color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-welcome-action:hover { background: var(--rm-hover); }\n.rm-welcome-action b { display: block; font-size: 13px; }\n.rm-welcome-action span { display: block; font-size: 12px; color: var(--rm-quiet); margin-top: 2px; }\n\n/* \u2500\u2500 avatars \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-avatar {\n display: inline-flex; align-items: center; justify-content: center; flex: none;\n font-weight: 600; letter-spacing: .02em; user-select: none;\n}\n.rm-avatar-wrap { position: relative; display: inline-flex; flex: none; }\n\n/* \u2500\u2500 dialogs (inside dsh Modal) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-form { display: flex; flex-direction: column; gap: 14px; }\n.rm-field { display: flex; flex-direction: column; gap: 6px; }\n.rm-field-label { font-size: 13px; font-weight: 500; }\n.rm-field-hint { font-size: 12px; color: var(--rm-quiet); }\n.rm-name-row { display: flex; align-items: center; gap: 10px; }\n.rm-name-row > :last-child { flex: 1; }\n.rm-switch-row {\n display: flex; align-items: center; justify-content: space-between; gap: 14px;\n padding: 10px 12px; border-radius: 10px; background: var(--rm-panel);\n}\n.rm-error { color: var(--dsw-alias-state-error-primary, #e5484d); font-size: 12.5px; }\n.rm-invite {\n display: block; padding: 10px 12px; border-radius: 8px; background: var(--rm-panel);\n font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace);\n font-size: 12px; word-break: break-all; user-select: all;\n}\n\n/* Dual transport + approvals + agent config */\n.rm-mode-row { display: flex; gap: 8px; }\n.rm-mode-btn {\n flex: 1; padding: 10px 12px; border-radius: 10px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer; font: inherit;\n}\n.rm-mode-btn.is-on { border-color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent); }\n.rm-lan-browse { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; align-items: center; }\n.rm-lan-peer {\n font-size: 12px; padding: 4px 8px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer;\n}\n.rm-approvals { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); background: color-mix(in srgb, #f5a524 12%, transparent); }\n.rm-approval { display: flex; gap: 12px; align-items: center; justify-content: space-between; }\n.rm-approval-body { font-size: 13px; }\n.rm-approval-actions { display: flex; gap: 6px; flex-shrink: 0; }\n.rm-input { width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; font: inherit; }\n\n.rm-ws-bar { display: flex; flex-direction: column; gap: 4px; padding: 8px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); }\n.rm-ws-chip { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; cursor: pointer; font: inherit; font-size: 12.5px; }\n.rm-ws-path { font-size: 11px; color: var(--rm-quiet, #8b93a7); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item.is-active { background: color-mix(in srgb, var(--rm-accent, #5b8cff) 12%, transparent); }\n\n/* Native-aligned room composer (mirrors dsh conversation control row) */\n.rm-composer-card {\n box-sizing: border-box;\n width: 100%;\n max-width: 920px;\n margin: 0 auto;\n border-radius: 16px;\n background: var(--rm-elev, var(--rm-panel, #12151c));\n border: 1px solid var(--rm-border, #2a2f3a);\n box-shadow: 0 1px 2px rgba(0,0,0,.06);\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 8px 0 6px;\n}\n.rm-composer-grow { position: relative; padding: 0 4px; }\n.rm-composer-grow textarea {\n box-sizing: border-box; width: 100%; min-height: 36px; max-height: 160px;\n resize: none; border: none; outline: none; background: transparent;\n color: inherit; font: inherit; font-size: 14px; line-height: 22px;\n padding: 4px 12px 0 14px;\n}\n.rm-composer-grow textarea:disabled { opacity: .55; cursor: not-allowed; }\n.rm-composer-row {\n display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;\n gap: 10px; min-width: 0; padding: 2px 8px 4px;\n}\n.rm-composer-tools, .rm-composer-modes, .rm-composer-trailing {\n display: flex; align-items: center; min-width: 0; gap: 8px;\n}\n.rm-composer-trailing { flex: none; margin-left: auto; }\n.rm-composer-add {\n width: 28px; height: 28px; border: none; border-radius: 999px;\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n color: inherit; cursor: pointer; display: grid; place-items: center;\n}\n.rm-composer-add:hover, .rm-composer-add[aria-pressed="true"] {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 22%, transparent);\n}\n.rm-composer-select {\n max-width: 220px; height: 28px; border: none; border-radius: 8px;\n background: transparent; color: var(--rm-quiet, #8b93a7);\n font: inherit; font-size: 13px; font-weight: 500; line-height: 20px;\n padding: 0 8px; cursor: pointer; white-space: nowrap;\n}\n.rm-composer-select:hover:not(:disabled) {\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n}\n.rm-composer-select:disabled { opacity: .5; cursor: default; }\n.rm-composer-model-text { display: inline; }\n.rm-composer-model-icon { display: none; font-size: 10px; }\n.rm-composer-effort {\n margin-left: 6px; font-size: 11px; opacity: .75;\n text-transform: uppercase; letter-spacing: .02em;\n}\n.rm-composer-send {\n width: 34px; height: 34px; border: none; border-radius: 999px;\n background: var(--rm-accent, #5b8cff); color: #fff; cursor: pointer;\n display: grid; place-items: center; transform: translateY(-2px);\n}\n.rm-composer-send:hover:not(:disabled) { filter: brightness(1.08); }\n.rm-composer-send:disabled { opacity: .4; cursor: default; }\n.rm-composer-popwrap { position: relative; }\n.rm-composer-menu {\n position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 40;\n min-width: 200px; max-height: 320px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 8px 24px rgba(0,0,0,.28);\n padding: 6px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-composer-menu-wide { min-width: 280px; max-width: 340px; }\n.rm-composer-menu-label {\n font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;\n color: var(--rm-quiet, #8b93a7); padding: 6px 8px 2px;\n}\n.rm-composer-menu button[role="menuitem"] {\n text-align: left; border: none; background: transparent; color: inherit;\n font: inherit; font-size: 13px; padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-composer-menu button[role="menuitem"]:hover,\n.rm-composer-menu button[role="menuitem"].is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent);\n}\n.rm-composer-check {\n display: flex; align-items: center; gap: 8px; font-size: 13px; padding: 4px 8px; cursor: pointer;\n}\n.rm-grant-row {\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n padding: 2px 8px; font-size: 12.5px;\n}\n.rm-grant-row select {\n font: inherit; font-size: 12px; border-radius: 6px;\n border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; padding: 2px 6px;\n}\n.rm-peer-grant-block { border-top: 1px solid var(--rm-border, #2a2f3a); padding-top: 4px; margin-top: 4px; }\n.rm-peer-grant-name { font-size: 12px; font-weight: 600; padding: 4px 8px; overflow: hidden; text-overflow: ellipsis; }\n\n/* Hide legacy tight composer if still referenced */\n.rm-composer { display: none; }\n\n.rm-composer-grow { position: relative; }\n.rm-mention-menu {\n position: absolute; left: 8px; right: 8px; bottom: calc(100% + 6px); z-index: 50;\n max-height: 240px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 10px 28px rgba(0,0,0,.32);\n padding: 4px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-mention-item {\n display: flex; align-items: center; gap: 8px; text-align: left;\n border: none; background: transparent; color: inherit; font: inherit;\n padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-mention-item:hover, .rm-mention-item.is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 18%, transparent);\n}\n.rm-mention-item-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }\n.rm-mention-item-name { font-size: 13px; font-weight: 600; }\n.rm-mention-item-meta { font-size: 11px; color: var(--rm-quiet, #8b93a7); }\n\n\n/* New Room shortcut under workspace (mirrors New Session affordance) */\n.rm-new-room {\n display: flex; align-items: center; justify-content: center; gap: 6px;\n width: 100%; height: 36px; margin-top: 8px;\n border: .5px solid var(--rm-border); border-radius: 10px;\n background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n color: var(--rm-label); cursor: pointer; font-weight: 500; font-size: 13.5px;\n}\n.rm-new-room:hover { background: var(--dsw-alias-button-floating-hover, var(--rm-hover)); }\n.rm-new-room svg { flex: none; }\n\n/* Wide / landscape create + browse modals (overrides default 380px Modal card) */\n.rm-modal-wide {\n width: min(720px, calc(100vw - 48px)) !important;\n max-height: min(640px, calc(100vh - 48px));\n}\n.rm-form-wide {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px 18px;\n align-items: start;\n}\n.rm-form-wide .rm-span-2 { grid-column: 1 / -1; }\n.rm-browse { display: flex; flex-direction: column; gap: 12px; min-height: 280px; max-height: 420px; }\n.rm-browse-search { flex: none; }\n.rm-browse-status { font-size: 11.5px; color: var(--rm-quiet); white-space: nowrap; }\n.rm-browse-list { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 6px; }\n@media (max-width: 640px) {\n .rm-form-wide { grid-template-columns: 1fr; }\n .rm-modal-wide { width: min(420px, calc(100vw - 32px)) !important; }\n}\n';
1548
1532
 
1549
1533
  // src/client/store.js
1550
1534
  var API = "/api/collab";
@@ -1622,7 +1606,13 @@ function createRoomsStore() {
1622
1606
  if (text) p.set("q", text);
1623
1607
  if (tag) p.set("tag", tag);
1624
1608
  const r = await call(`/directory?${p}`);
1625
- set((s) => s.search.query === query ? { search: { ...s.search, results: r.rooms, loading: false } } : {});
1609
+ const rooms = Array.isArray(r.rooms) ? r.rooms.map((x) => ({
1610
+ ...x,
1611
+ tags: Array.isArray(x.tags) ? x.tags : [],
1612
+ members: typeof x.members === "number" ? x.members : 0,
1613
+ description: x.description || ""
1614
+ })) : [];
1615
+ set((s) => s.search.query === query ? { search: { ...s.search, results: rooms, loading: false, error: null } } : {});
1626
1616
  } catch (e) {
1627
1617
  set((s) => ({ search: { ...s.search, loading: false, error: e.message } }));
1628
1618
  }
@@ -1798,9 +1788,9 @@ function RoomsSidebar({ store, ensurePanel }) {
1798
1788
  const s = useStore(store);
1799
1789
  const a = store.actions;
1800
1790
  const [menu, setMenu] = (0, import_react.useState)(null);
1801
- const [searching, setSearching] = (0, import_react.useState)(false);
1802
1791
  const [joining, setJoining] = (0, import_react.useState)(null);
1803
1792
  const inputRef = (0, import_react.useRef)(null);
1793
+ const searching = !!s.search.open;
1804
1794
  const activeWs = (s.workspaces || []).find((w) => w.id === s.activeWorkspaceId) || (s.workspaces || [])[0] || null;
1805
1795
  (0, import_react.useEffect)(() => {
1806
1796
  if (!menu) return;
@@ -1840,10 +1830,7 @@ function RoomsSidebar({ store, ensurePanel }) {
1840
1830
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-side", "data-collab-sidebar": "", children: [
1841
1831
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-side-toolbar", children: [
1842
1832
  /* @__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, {}) }),
1833
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-icon-btn", "aria-label": searching ? "Close search" : "Search rooms", "aria-pressed": searching, onClick: () => a.setSearchOpen(!searching), children: searching ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CloseIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}) }),
1847
1834
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1848
1835
  "button",
1849
1836
  {
@@ -1885,8 +1872,7 @@ function RoomsSidebar({ store, ensurePanel }) {
1885
1872
  ] }),
1886
1873
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "menuitem", onClick: () => {
1887
1874
  setMenu(null);
1888
- setSearching(true);
1889
- a.setSearchOpen(true);
1875
+ a.openDialog({ kind: "browse" });
1890
1876
  }, children: [
1891
1877
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
1892
1878
  "Browse public rooms"
@@ -1898,7 +1884,14 @@ function RoomsSidebar({ store, ensurePanel }) {
1898
1884
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
1899
1885
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: activeWs.name })
1900
1886
  ] }),
1901
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-path", title: activeWs.path, children: activeWs.path })
1887
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-path", title: activeWs.path, children: activeWs.path }),
1888
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-new-room", "data-rm-new-room": "", onClick: () => {
1889
+ a.openDialog({ kind: "create" });
1890
+ ensurePanel();
1891
+ }, children: [
1892
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}),
1893
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "New Room" })
1894
+ ] })
1902
1895
  ] }),
1903
1896
  searching ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search", children: [
1904
1897
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
@@ -1974,7 +1967,7 @@ function RoomsSidebar({ store, ensurePanel }) {
1974
1967
  r.members,
1975
1968
  " online"
1976
1969
  ] }),
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: [
1970
+ (r.tags || []).length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-dir-tags", children: (r.tags || []).map((tg) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-tag", children: [
1978
1971
  "#",
1979
1972
  tg
1980
1973
  ] }, tg)) }) : null
@@ -2095,23 +2088,23 @@ function MentionMenu({ items, active, onPick, onHover }) {
2095
2088
  it.id
2096
2089
  )) });
2097
2090
  }
2098
- function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
2091
+ function Composer({ room, store, draft, setDraft }) {
2099
2092
  const a = store.actions;
2100
2093
  const [mode, setMode] = (0, import_react.useState)("chat");
2101
2094
  const [busy, setBusy] = (0, import_react.useState)(false);
2102
2095
  const [modelOpen, setModelOpen] = (0, import_react.useState)(false);
2103
2096
  const [permOpen, setPermOpen] = (0, import_react.useState)(false);
2104
2097
  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);
2098
+ const [mentionActive, setMentionActive] = (0, import_react.useState)(0);
2099
+ const [caret, setCaret] = (0, import_react.useState)(0);
2107
2100
  const [mentionDismissed, setMentionDismissed] = (0, import_react.useState)(false);
2108
- const taRef2 = (0, import_react.useRef)(null);
2101
+ const taRef = (0, import_react.useRef)(null);
2109
2102
  const offline = room.status !== "online";
2110
- const mqRaw = mentionQueryAt(draft2, caret);
2111
- const mq2 = mentionDismissed ? null : mqRaw;
2103
+ const mqRaw = mentionQueryAt(draft, caret);
2104
+ const mq = mentionDismissed ? null : mqRaw;
2112
2105
  const mentionItems = (() => {
2113
- if (!mq2) return [];
2114
- const q = mq2.query.toLowerCase();
2106
+ if (!mq) return [];
2107
+ const q = mq.query.toLowerCase();
2115
2108
  const members = (room.members || []).filter((m) => !m.self);
2116
2109
  const known = members.map((m) => ({
2117
2110
  id: m.agentId,
@@ -2135,7 +2128,7 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
2135
2128
  return bits.length ? `Auto \xB7 ${bits.join("+")}` : "Ask to run";
2136
2129
  })();
2137
2130
  (0, import_react.useEffect)(() => {
2138
- setMentionActive2(0);
2131
+ setMentionActive(0);
2139
2132
  setMentionDismissed(false);
2140
2133
  }, [mqRaw && mqRaw.query, mqRaw && mqRaw.start]);
2141
2134
  const MODELS = ["deepseek-chat", "deepseek-reasoner", "deepseek-coder"];
@@ -2158,28 +2151,8 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
2158
2151
  a.toast(e.message);
2159
2152
  }
2160
2153
  };
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
2154
  const submit = async () => {
2182
- const text = draft2.trim();
2155
+ const text = draft.trim();
2183
2156
  if (!text || busy) return;
2184
2157
  setBusy(true);
2185
2158
  try {
@@ -2188,7 +2161,7 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
2188
2161
  await a.task(room.roomId, m ? text.slice(m[0].length) : text, m ? m[1] : void 0);
2189
2162
  setMode("chat");
2190
2163
  } else await a.send(room.roomId, text);
2191
- setDraft2("");
2164
+ setDraft("");
2192
2165
  } catch (e) {
2193
2166
  a.toast(e.message);
2194
2167
  } finally {
@@ -2197,33 +2170,33 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
2197
2170
  };
2198
2171
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-composer-card", "data-rm-composer": "", children: [
2199
2172
  /* @__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,
2173
+ mq && mentionItems.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MentionMenu, { items: mentionItems, active: mentionActive, onHover: setMentionActive, onPick: insertMention }) : null,
2201
2174
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2202
2175
  "textarea",
2203
2176
  {
2204
- ref: taRef2,
2177
+ ref: taRef,
2205
2178
  rows: 1,
2206
2179
  disabled: offline,
2207
2180
  "aria-label": "Message the room",
2208
2181
  placeholder: offline ? "Room is offline" : mode === "task" ? "Task: @assignee title (optional assignee)" : `Message ${room.name} \xB7 @agent or @all`,
2209
- value: draft2,
2182
+ value: draft,
2210
2183
  onChange: (e) => {
2211
- setDraft2(e.target.value);
2184
+ setDraft(e.target.value);
2212
2185
  syncCaret(e.target);
2213
2186
  },
2214
2187
  onClick: (e) => syncCaret(e.target),
2215
2188
  onKeyUp: (e) => syncCaret(e.target),
2216
2189
  onSelect: (e) => syncCaret(e.target),
2217
2190
  onKeyDown: (e) => {
2218
- if (mq2 && mentionItems.length) {
2191
+ if (mq && mentionItems.length) {
2219
2192
  if (e.key === "ArrowDown") {
2220
2193
  e.preventDefault();
2221
- setMentionActive2((i) => (i + 1) % mentionItems.length);
2194
+ setMentionActive((i) => (i + 1) % mentionItems.length);
2222
2195
  return;
2223
2196
  }
2224
2197
  if (e.key === "ArrowUp") {
2225
2198
  e.preventDefault();
2226
- setMentionActive2((i) => (i - 1 + mentionItems.length) % mentionItems.length);
2199
+ setMentionActive((i) => (i - 1 + mentionItems.length) % mentionItems.length);
2227
2200
  return;
2228
2201
  }
2229
2202
  if (e.key === "Enter" || e.key === "Tab") {
@@ -2309,7 +2282,7 @@ function Composer({ room, store, draft: draft2, setDraft: setDraft2 }) {
2309
2282
  peerOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PeerGrantsMenu, { room, store, onClose: () => setPeerOpen(false), patchConfig }) : null
2310
2283
  ] })
2311
2284
  ] }),
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, {}) }) })
2285
+ /* @__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
2286
  ] })
2314
2287
  ] });
2315
2288
  }
@@ -2474,11 +2447,11 @@ function EmptyMain({ store }) {
2474
2447
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Paste a collab://join/\u2026 invite" })
2475
2448
  ] })
2476
2449
  ] }),
2477
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.setSearchOpen(true), children: [
2450
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "browse" }), children: [
2478
2451
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
2479
2452
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
2480
2453
  /* @__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" })
2454
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Search the VPS directory by name or #tag" })
2482
2455
  ] })
2483
2456
  ] })
2484
2457
  ] })
@@ -2490,8 +2463,8 @@ function RoomsMain({ store }) {
2490
2463
  const room = s.rooms.find((r) => r.roomId === s.selected);
2491
2464
  if (!room) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyMain, { store });
2492
2465
  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 }));
2466
+ const draft = drafts[room.roomId] || "";
2467
+ const setDraft = (v) => setDrafts((d) => ({ ...d, [room.roomId]: typeof v === "function" ? v(d[room.roomId] || "") : v }));
2495
2468
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-main", "data-collab-main": "", children: [
2496
2469
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApprovalBanner, { store }),
2497
2470
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-workspace", children: [
@@ -2518,7 +2491,7 @@ function RoomsMain({ store }) {
2518
2491
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: room.status === "online" ? "done" : room.status === "connecting" ? "ongoing" : "error", size: 8 })
2519
2492
  ] }),
2520
2493
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages }),
2521
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Composer, { room, store, draft: draft2, setDraft: setDraft2 })
2494
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Composer, { room, store, draft, setDraft })
2522
2495
  ] }),
2523
2496
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2524
2497
  RoomDetail,
@@ -2526,9 +2499,9 @@ function RoomsMain({ store }) {
2526
2499
  room,
2527
2500
  messages,
2528
2501
  store,
2529
- mention: (id) => setDraft2((d) => `${d}${d && !d.endsWith(" ") ? " " : ""}@${id} `),
2502
+ mention: (id) => setDraft((d) => `${d}${d && !d.endsWith(" ") ? " " : ""}@${id} `),
2530
2503
  assign: (id) => {
2531
- setDraft2(`@${id} `);
2504
+ setDraft(`@${id} `);
2532
2505
  store.actions.toast(`Switch to task mode, then describe the task for @${id}`);
2533
2506
  }
2534
2507
  }
@@ -2551,26 +2524,7 @@ function CreateDialog({ store }) {
2551
2524
  const [busy, setBusy] = (0, import_react.useState)(false);
2552
2525
  const [lanPeers, setLanPeers] = (0, import_react.useState)([]);
2553
2526
  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
- };
2527
+ const ws = (s.workspaces || []).find((w) => w.id === s.activeWorkspaceId) || (s.workspaces || [])[0];
2574
2528
  const submit = async () => {
2575
2529
  if (!ok) return;
2576
2530
  setBusy(true);
@@ -2604,23 +2558,24 @@ function CreateDialog({ store }) {
2604
2558
  onClose: a.closeDialog,
2605
2559
  title: "Create room",
2606
2560
  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.",
2561
+ className: "rm-modal-wide",
2562
+ description: ws ? `In workspace \u201C${ws.name}\u201D. Default relay 62.234.113.56:80 \xB7 E2EE \xB7 keys stay local.` : "Bind a workspace first. Default relay 62.234.113.56:80.",
2608
2563
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2609
2564
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
2610
2565
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: !ok, onClick: submit, children: busy ? "Creating\u2026" : "Create" })
2611
2566
  ] }),
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
- ] }) }),
2567
+ 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: [
2617
2568
  /* @__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
2569
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: f.name || "room", size: 36 }),
2619
2570
  /* @__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
2571
  ] }) }),
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: [
2572
+ /* @__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: [
2573
+ /* @__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" }),
2574
+ /* @__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" })
2575
+ ] }) }),
2576
+ /* @__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 }) }) }),
2577
+ /* @__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 }) }) }),
2578
+ 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
2579
  /* @__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
2580
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-lan-browse", children: [
2626
2581
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: browseLan, children: "Browse mDNS" }),
@@ -2629,15 +2584,15 @@ function CreateDialog({ store }) {
2629
2584
  p.name ? ` \xB7 ${p.name}` : ""
2630
2585
  ] }, p.hostPort))
2631
2586
  ] })
2632
- ] }) : null,
2633
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
2587
+ ] }) }) : null,
2588
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-switch-row", children: [
2634
2589
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
2635
2590
  /* @__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." })
2591
+ /* @__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
2592
  ] }),
2638
2593
  /* @__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
2594
  ] }),
2640
- err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: err }) : null
2595
+ err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-error", children: err }) : null
2641
2596
  ] })
2642
2597
  }
2643
2598
  );
@@ -2649,26 +2604,6 @@ function JoinDialog({ store }) {
2649
2604
  const [err, setErr] = (0, import_react.useState)(null);
2650
2605
  const [busy, setBusy] = (0, import_react.useState)(false);
2651
2606
  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
- };
2672
2607
  const submit = async () => {
2673
2608
  if (!valid || busy) return;
2674
2609
  setBusy(true);
@@ -2773,26 +2708,6 @@ function ConfigDialog({ store, d }) {
2773
2708
  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
2709
  });
2775
2710
  }, [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
2711
  const submit = async () => {
2797
2712
  setBusy(true);
2798
2713
  setErr(null);
@@ -2894,26 +2809,6 @@ function WorkspaceDialog({ store }) {
2894
2809
  const [path, setPath] = (0, import_react.useState)("");
2895
2810
  const [err, setErr] = (0, import_react.useState)(null);
2896
2811
  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
2812
  const submit = async () => {
2918
2813
  if (!path.trim()) {
2919
2814
  setErr("Absolute workspace path required");
@@ -2954,6 +2849,78 @@ function WorkspaceDialog({ store }) {
2954
2849
  }
2955
2850
  );
2956
2851
  }
2852
+ function BrowseDialog({ store }) {
2853
+ const s = useStore(store);
2854
+ const a = store.actions;
2855
+ const [joining, setJoining] = (0, import_react.useState)(null);
2856
+ const inputRef = (0, import_react.useRef)(null);
2857
+ const activeWs = (s.workspaces || []).find((w) => w.id === s.activeWorkspaceId) || (s.workspaces || [])[0];
2858
+ (0, import_react.useEffect)(() => {
2859
+ a.setSearchOpen(true);
2860
+ requestAnimationFrame(() => inputRef.current?.focus());
2861
+ return () => {
2862
+ };
2863
+ }, []);
2864
+ const join = async (r) => {
2865
+ setJoining(r.roomId);
2866
+ try {
2867
+ await a.join(r.invite, { name: r.name, description: r.description, tags: r.tags, public: true, workspaceId: activeWs?.id });
2868
+ } catch (e) {
2869
+ a.toast(e.message);
2870
+ } finally {
2871
+ setJoining(null);
2872
+ }
2873
+ };
2874
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2875
+ import_dsh_client_ui_primitives.Modal,
2876
+ {
2877
+ open: true,
2878
+ onClose: a.closeDialog,
2879
+ title: "Browse public rooms",
2880
+ closeLabel: "Close",
2881
+ className: "rm-modal-wide",
2882
+ description: "Directory on VPS 62.234.113.56:80. Search by name or #tag, then join into the active workspace.",
2883
+ footer: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Close" }),
2884
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-browse", children: [
2885
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search rm-browse-search", children: [
2886
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
2887
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2888
+ "input",
2889
+ {
2890
+ ref: inputRef,
2891
+ type: "text",
2892
+ placeholder: "Search rooms or #tags",
2893
+ value: s.search.query,
2894
+ onChange: (e) => a.setQuery(e.target.value),
2895
+ "aria-label": "Search public rooms"
2896
+ }
2897
+ ),
2898
+ s.search.loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-browse-status", children: "Searching\u2026" }) : null
2899
+ ] }),
2900
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-browse-list", children: [
2901
+ s.search.results.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-dir-item", "data-dir-room-id": r.roomId, children: [
2902
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: r.name, size: 30 }),
2903
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-dir-item-body", children: [
2904
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-room-item-name", children: r.name }),
2905
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-room-item-meta", children: [
2906
+ r.description || "No description",
2907
+ " \xB7 ",
2908
+ r.members,
2909
+ " online"
2910
+ ] }),
2911
+ (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: [
2912
+ "#",
2913
+ tg
2914
+ ] }, tg)) }) : null
2915
+ ] }),
2916
+ /* @__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" })
2917
+ ] }, r.roomId)),
2918
+ !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
2919
+ ] })
2920
+ ] })
2921
+ }
2922
+ );
2923
+ }
2957
2924
  function Overlay({ store }) {
2958
2925
  const s = useStore(store);
2959
2926
  const d = s.dialog;
@@ -2964,6 +2931,7 @@ function Overlay({ store }) {
2964
2931
  d?.kind === "leave" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LeaveDialog, { store, d }) : null,
2965
2932
  d?.kind === "config" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConfigDialog, { store, d }) : null,
2966
2933
  d?.kind === "workspace" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceDialog, { store }) : null,
2934
+ d?.kind === "browse" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BrowseDialog, { store }) : null,
2967
2935
  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
2936
  ] });
2969
2937
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cecwxf/dsh-room",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "DSH Room \u2014 DeepSeek Harness plugin for accountless E2EE Collab rooms (sidebar: DSH Room)",
5
5
  "type": "module",
6
6
  "license": "MIT",