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