@cecwxf/dsh-room 0.1.3 → 0.3.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 +19 -1
- package/dist/client.js +1650 -612
- package/dist/index.js +1585 -84
- package/dist/types/adapter.d.ts +2 -0
- package/dist/types/adapter.d.ts.map +1 -1
- package/dist/types/command-handler.d.ts.map +1 -1
- package/dist/types/commands.d.ts +16 -0
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/http.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/isolation.d.ts +82 -0
- package/dist/types/isolation.d.ts.map +1 -0
- package/dist/types/local-agents.d.ts +59 -0
- package/dist/types/local-agents.d.ts.map +1 -0
- package/dist/types/orchestrator.d.ts +96 -0
- package/dist/types/orchestrator.d.ts.map +1 -0
- package/dist/types/plugin.d.ts +349 -11
- package/dist/types/plugin.d.ts.map +1 -1
- package/dist/types/state.d.ts +7 -0
- package/dist/types/state.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\n/* \u2500\u2500 local multi-agent (0.3) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-dot-busy { background: var(--dsw-alias-state-warning-primary, #f0a020); }\n.rm-agent-tag { display: inline-flex; align-items: center; gap: 3px; margin-left: 6px; padding: 0 6px; border-radius: 999px; font-size: 10.5px; line-height: 17px; color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 12%, transparent); white-space: nowrap; }\n.rm-msg-id { margin-left: 6px; font-size: 11px; color: var(--rm-quiet); font-family: var(--dsw-font-mono, ui-monospace, monospace); }\n.rm-agent-row { display: flex; align-items: center; gap: 8px; padding: 6px 4px; border-radius: 8px; }\n.rm-agent-row:hover { background: var(--rm-hover); }\n.rm-agent-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }\n.rm-agent-name { font-size: 13px; font-weight: 600; color: var(--rm-label); display: flex; align-items: center; }\n.rm-agent-sub { font-size: 11px; color: var(--rm-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.rm-agent-row .rm-member-actions { display: inline-flex !important; opacity: 0.65; }\n.rm-agent-row:hover .rm-member-actions { opacity: 1; }\n.rm-num-row { display: flex; gap: 8px; margin: 4px 0 8px; }\n.rm-num-setting { flex: 1; display: flex; flex-direction: column; gap: 3px; font-size: 11px; color: var(--rm-muted); }\n.rm-num-setting input { width: 100%; box-sizing: border-box; padding: 5px 8px; border: 1px solid var(--rm-border); border-radius: 7px; background: var(--rm-bg); color: var(--rm-label); font: inherit; font-size: 13px; }\n.rm-runs { display: flex; flex-direction: column; gap: 4px; }\n.rm-run { display: grid; grid-template-columns: 22px auto 1fr auto; align-items: center; gap: 6px; padding: 5px 6px; border-radius: 7px; font-size: 12px; border: 1px solid var(--rm-border); }\n.rm-run-running { border-color: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #f0a020) 55%, transparent); background: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #f0a020) 8%, transparent); }\n.rm-run-queued, .rm-run-approval { border-style: dashed; }\n.rm-run-done, .rm-run-stopped, .rm-run-failed { opacity: 0.7; }\n.rm-run-pos { font-size: 11px; color: var(--rm-quiet); text-align: center; font-variant-numeric: tabular-nums; }\n.rm-run-who { font-weight: 600; color: var(--rm-label); white-space: nowrap; }\n.rm-run-what { color: var(--rm-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }\n.rm-stop-btn { display: inline-flex; align-items: center; gap: 5px; height: 26px; padding: 0 10px; border-radius: 999px; border: 1px solid color-mix(in srgb, var(--dsw-alias-state-danger-primary, #e5484d) 60%, transparent); background: color-mix(in srgb, var(--dsw-alias-state-danger-primary, #e5484d) 10%, transparent); color: var(--dsw-alias-state-danger-primary, #e5484d); font-size: 12px; cursor: pointer; white-space: nowrap; }\n.rm-stop-btn:hover { background: color-mix(in srgb, var(--dsw-alias-state-danger-primary, #e5484d) 18%, transparent); }\n.rm-badge-local { color: var(--dsw-alias-state-success-primary, #2bb673); }\n.rm-session-agent { color: var(--rm-accent, #5b8cff); }\n.rm-preset-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }\n.rm-preset { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-width: 150px; flex: 1; padding: 8px 10px; border: 1px solid var(--rm-border); border-radius: 10px; background: transparent; color: var(--rm-label); text-align: left; cursor: pointer; font: inherit; font-size: 12px; }\n.rm-preset span { color: var(--rm-muted); font-size: 11px; line-height: 1.35; }\n.rm-preset.is-on { border-color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 12%, transparent); }\n.rm-preset-check { flex-direction: row; align-items: center; gap: 8px; min-width: 120px; flex: 0 1 auto; }\n.rm-local-box { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border: 1px dashed var(--rm-border); border-radius: 12px; }\n.rm-textarea { width: 100%; box-sizing: border-box; min-height: 84px; resize: vertical; font: inherit; font-size: 13px; padding: 8px 10px; }\n.rm-switch-col { display: flex; flex-direction: column; gap: 6px; }\n.rm-modal-xl { width: min(1180px, 96vw) !important; max-width: 96vw !important; }\n.rm-merge { display: grid; grid-template-columns: 260px 1fr; gap: 14px; min-height: 420px; }\n.rm-merge-list { display: flex; flex-direction: column; gap: 6px; border-right: 1px solid var(--rm-border); padding-right: 12px; }\n.rm-merge-agent { display: flex; align-items: center; gap: 8px; padding: 8px; border: 1px solid transparent; border-radius: 10px; background: transparent; color: var(--rm-label); cursor: pointer; text-align: left; font: inherit; }\n.rm-merge-agent:hover { background: var(--rm-hover); }\n.rm-merge-agent.is-on { border-color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 10%, transparent); }\n.rm-merge-agent-body { flex: 1; min-width: 0; display: flex; flex-direction: column; font-size: 12px; }\n.rm-merge-agent-body .rm-mono { font-size: 10.5px; color: var(--rm-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.rm-merge-main { min-width: 0; display: flex; flex-direction: column; gap: 8px; }\n.rm-merge-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; font-size: 13px; }\n.rm-merge-files { display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: 12px; }\n.rm-confirm-text { font-size: 12px; color: var(--rm-label); margin-right: 4px; }\n.rm-diff { margin: 0; max-height: 52vh; overflow: auto; padding: 8px 0; border: 1px solid var(--rm-border); border-radius: 10px; background: var(--rm-panel); font-family: var(--dsw-font-mono, ui-monospace, monospace); font-size: 12px; line-height: 1.5; }\n.rm-diff > div { padding: 0 12px; white-space: pre; }\n.rm-diff-add { background: color-mix(in srgb, #2bb673 16%, transparent); }\n.rm-diff-del { background: color-mix(in srgb, #e5484d 16%, transparent); }\n.rm-diff-hunk { color: var(--rm-accent, #5b8cff); }\n.rm-diff-head, .rm-diff-file { color: var(--rm-muted); font-weight: 600; }\n.rm-diff-addn { color: #2bb673; }\n.rm-diff-deln { color: #e5484d; }\n.rm-ok { padding: 8px 10px; border-radius: 8px; font-size: 12.5px; color: var(--dsw-alias-state-success-primary, #2bb673); background: color-mix(in srgb, #2bb673 10%, transparent); }\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}`);
|
|
@@ -207,9 +215,40 @@ function createRoomsStore() {
|
|
|
207
215
|
},
|
|
208
216
|
async mcp(roomId, tool, args, target) {
|
|
209
217
|
return post(`/rooms/${encodeURIComponent(roomId)}/mcp`, { tool, args, target });
|
|
218
|
+
},
|
|
219
|
+
// ── local multi-agent ──
|
|
220
|
+
async presets() {
|
|
221
|
+
return (await call("/presets")).presets;
|
|
222
|
+
},
|
|
223
|
+
async addAgent(roomId, body) {
|
|
224
|
+
return post(`/rooms/${encodeURIComponent(roomId)}/agents`, body);
|
|
225
|
+
},
|
|
226
|
+
async updateAgent(roomId, id, body) {
|
|
227
|
+
return post(`/rooms/${encodeURIComponent(roomId)}/agents/${encodeURIComponent(id)}`, body);
|
|
228
|
+
},
|
|
229
|
+
async removeAgent(roomId, id) {
|
|
230
|
+
return post(`/rooms/${encodeURIComponent(roomId)}/agents/${encodeURIComponent(id)}?op=delete`);
|
|
231
|
+
},
|
|
232
|
+
async saveLocal(roomId, patch) {
|
|
233
|
+
return post(`/rooms/${encodeURIComponent(roomId)}/local`, patch);
|
|
234
|
+
},
|
|
235
|
+
async setTransport(roomId, target, force) {
|
|
236
|
+
return post(`/rooms/${encodeURIComponent(roomId)}/transport`, { target, force: !!force });
|
|
237
|
+
},
|
|
238
|
+
async stop(roomId, agentId) {
|
|
239
|
+
return post(`/rooms/${encodeURIComponent(roomId)}/stop`, agentId ? { agentId } : {});
|
|
240
|
+
},
|
|
241
|
+
async diffs(roomId) {
|
|
242
|
+
return call(`/rooms/${encodeURIComponent(roomId)}/diffs`);
|
|
243
|
+
},
|
|
244
|
+
async merge(roomId, agentId) {
|
|
245
|
+
return post(`/rooms/${encodeURIComponent(roomId)}/merge`, { agentId, approve: true });
|
|
246
|
+
},
|
|
247
|
+
async discard(roomId, agentId) {
|
|
248
|
+
return post(`/rooms/${encodeURIComponent(roomId)}/discard`, { agentId, approve: true });
|
|
210
249
|
}
|
|
211
250
|
};
|
|
212
|
-
|
|
251
|
+
const api = {
|
|
213
252
|
getSnapshot: () => snap,
|
|
214
253
|
subscribe(fn) {
|
|
215
254
|
subs.add(fn);
|
|
@@ -222,14 +261,20 @@ function createRoomsStore() {
|
|
|
222
261
|
es = null;
|
|
223
262
|
subs.clear();
|
|
224
263
|
clearTimeout(searchTimer);
|
|
225
|
-
}
|
|
264
|
+
},
|
|
265
|
+
/** Native dsh workspaces service (set by apply). */
|
|
266
|
+
native: null,
|
|
267
|
+
nativeList: null,
|
|
268
|
+
/** Called after a room is created/joined (apply switches to the DSH Room panel). */
|
|
269
|
+
onRoomOpened: null
|
|
226
270
|
};
|
|
271
|
+
return api;
|
|
227
272
|
}
|
|
228
273
|
|
|
229
274
|
// src/client/index.jsx
|
|
230
275
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
231
276
|
var name = "dsh-room-client";
|
|
232
|
-
var inject = ["slots", "layout"];
|
|
277
|
+
var inject = ["slots", "layout", "workspaces"];
|
|
233
278
|
var PANEL_ID = "collab";
|
|
234
279
|
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
280
|
var RoomsIcon = ({ size }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size, children: [
|
|
@@ -242,7 +287,6 @@ var SearchIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { child
|
|
|
242
287
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m10.2 10.2 3.3 3.3" })
|
|
243
288
|
] });
|
|
244
289
|
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
290
|
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
291
|
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
292
|
var TaskIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { children: [
|
|
@@ -257,6 +301,17 @@ var LockIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size: 1
|
|
|
257
301
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3.5", y: "7", width: "9", height: "6.25", rx: "1.5" }),
|
|
258
302
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5.5 7V5.25a2.5 2.5 0 0 1 5 0V7" })
|
|
259
303
|
] });
|
|
304
|
+
var BotIcon = ({ size = 14 }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size, children: [
|
|
305
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3", y: "5", width: "10", height: "7.5", rx: "2" }),
|
|
306
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 2.5V5M6 8.5h.01M10 8.5h.01M6.25 10.75h3.5" })
|
|
307
|
+
] });
|
|
308
|
+
var StopIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Svg, { size: 12, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5" }) });
|
|
309
|
+
var MergeIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size: 14, children: [
|
|
310
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "5", cy: "3.75", r: "1.5" }),
|
|
311
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "5", cy: "12.25", r: "1.5" }),
|
|
312
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "11", cy: "8", r: "1.5" }),
|
|
313
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 5.25v5.5M5 5.5c0 2 1.5 2.5 4.5 2.5" })
|
|
314
|
+
] });
|
|
260
315
|
var PeopleIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size: 14, children: [
|
|
261
316
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "5.5", r: "2.25" }),
|
|
262
317
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2.5 13c.4-2 1.8-3.25 3.5-3.25S9.1 11 9.5 13" }),
|
|
@@ -287,220 +342,133 @@ var STATUS_TONE = { open: "info", claimed: "warning", done: "success", failed: "
|
|
|
287
342
|
function useStore(store) {
|
|
288
343
|
return (0, import_react.useSyncExternalStore)(store.subscribe, store.getSnapshot);
|
|
289
344
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
|
|
345
|
+
var EMPTY_WS = { items: [] };
|
|
346
|
+
var NO_SUB = () => () => {
|
|
347
|
+
};
|
|
348
|
+
var NO_SNAP = () => EMPTY_WS;
|
|
349
|
+
function useNativeWorkspaces(store) {
|
|
350
|
+
const src = store.nativeList;
|
|
351
|
+
const snap = (0, import_react.useSyncExternalStore)(src ? src.subscribe : NO_SUB, src ? src.getSnapshot : NO_SNAP);
|
|
352
|
+
return snap && snap.items || [];
|
|
353
|
+
}
|
|
354
|
+
var samePath = (a, b) => !!a && !!b && a.replace(/[\\/]+$/, "") === b.replace(/[\\/]+$/, "");
|
|
355
|
+
function groupRooms(rooms, workspaces) {
|
|
356
|
+
const groups = workspaces.map((w) => ({ ws: w, rooms: [] }));
|
|
357
|
+
const other = [];
|
|
358
|
+
for (const r of rooms) {
|
|
359
|
+
const path = r.memberConfig && r.memberConfig.workspacePath;
|
|
360
|
+
const g = groups.find((x) => samePath(x.ws.path, path));
|
|
361
|
+
if (g) g.rooms.push(r);
|
|
362
|
+
else other.push(r);
|
|
363
|
+
}
|
|
364
|
+
return { groups, other };
|
|
365
|
+
}
|
|
366
|
+
var wsTitle = (w) => w.title || (w.path || "").split(/[\\/]/).filter(Boolean).pop() || w.path;
|
|
367
|
+
function RoomItem({ r, selected, onClick }) {
|
|
368
|
+
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: [
|
|
369
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-avatar-wrap", children: [
|
|
370
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: r.name, size: 26 }),
|
|
371
|
+
/* @__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)" } })
|
|
372
|
+
] }),
|
|
373
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-room-item-body", children: [
|
|
374
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-name", children: r.name }),
|
|
375
|
+
/* @__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") })
|
|
376
|
+
] }),
|
|
377
|
+
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, {}) })
|
|
378
|
+
] });
|
|
295
379
|
}
|
|
296
|
-
function
|
|
380
|
+
function RoomsNav({ store }) {
|
|
297
381
|
const s = useStore(store);
|
|
298
382
|
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
|
|
390
|
-
] }),
|
|
391
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-bar", children: [
|
|
392
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-ws-chip", onClick: () => a.openDialog({ kind: "workspace" }), title: activeWs.path, children: [
|
|
393
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
|
|
394
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: activeWs.name })
|
|
395
|
-
] }),
|
|
396
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-path", title: activeWs.path, children: activeWs.path }),
|
|
397
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-new-room", "data-rm-new-room": "", onClick: () => {
|
|
398
|
-
a.openDialog({ kind: "create" });
|
|
399
|
-
ensurePanel();
|
|
400
|
-
}, children: [
|
|
401
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}),
|
|
402
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "New Room" })
|
|
403
|
-
] })
|
|
383
|
+
const workspaces = useNativeWorkspaces(store);
|
|
384
|
+
const [q, setQ] = (0, import_react.useState)("");
|
|
385
|
+
const qq = q.trim().toLowerCase();
|
|
386
|
+
const match = (r) => !qq || r.name.toLowerCase().includes(qq) || (r.description || "").toLowerCase().includes(qq) || (r.tags || []).some((t) => ("#" + t).includes(qq));
|
|
387
|
+
const { groups, other } = groupRooms((s.rooms || []).filter(match), workspaces);
|
|
388
|
+
const sel = (r) => a.select(r.roomId);
|
|
389
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("nav", { className: "rm-nav", "aria-label": "Rooms by workspace", "data-rm-nav": "", children: [
|
|
390
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-nav-head", children: [
|
|
391
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-side-title", children: "Rooms" }),
|
|
392
|
+
/* @__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, {}) }),
|
|
393
|
+
/* @__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, {}) })
|
|
404
394
|
] }),
|
|
405
|
-
|
|
395
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search", children: [
|
|
406
396
|
/* @__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)(
|
|
397
|
+
/* @__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" })
|
|
398
|
+
] }),
|
|
399
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-nav-scroll", children: [
|
|
400
|
+
groups.map(({ ws, rooms }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-group", "data-rm-ws-group": ws.workspaceId, children: [
|
|
401
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-head", title: ws.path, children: [
|
|
402
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
|
|
403
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-name", children: wsTitle(ws) }),
|
|
404
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
417
405
|
"button",
|
|
418
406
|
{
|
|
419
407
|
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: [
|
|
408
|
+
className: "rm-icon-btn rm-ws-add",
|
|
409
|
+
title: `New room in ${wsTitle(ws)}`,
|
|
410
|
+
"aria-label": `New room in ${wsTitle(ws)}`,
|
|
411
|
+
onClick: () => a.openDialog({ kind: "create", workspace: { id: ws.workspaceId, path: ws.path, title: wsTitle(ws) } }),
|
|
412
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {})
|
|
413
|
+
}
|
|
414
|
+
)
|
|
415
|
+
] }),
|
|
416
|
+
rooms.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomItem, { r, selected: r.roomId === s.selected, onClick: () => sel(r) }, r.roomId)),
|
|
417
|
+
!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
|
|
418
|
+
] }, ws.workspaceId)),
|
|
419
|
+
other.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-group", children: [
|
|
420
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-head", children: [
|
|
457
421
|
/* @__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" : ""
|
|
422
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-name", children: "Other rooms" })
|
|
468
423
|
] }),
|
|
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
|
-
] })
|
|
424
|
+
other.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomItem, { r, selected: r.roomId === s.selected, onClick: () => sel(r) }, r.roomId))
|
|
425
|
+
] }) : null,
|
|
426
|
+
!workspaces.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-empty-hint", children: [
|
|
427
|
+
"No workspaces yet. Add one with the native \u201CAdd workspace\u201D in the left sidebar, then use \u201CNew room\u201D on it.",
|
|
428
|
+
/* @__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
429
|
] }) : null
|
|
493
430
|
] })
|
|
494
431
|
] });
|
|
495
432
|
}
|
|
496
|
-
function
|
|
433
|
+
function RoomSessionMark({ store, sessionId }) {
|
|
434
|
+
const s = useStore(store);
|
|
435
|
+
for (const r of s.rooms || []) {
|
|
436
|
+
const ag = (r.local && r.local.agents || []).find((x) => x.sessionId === sessionId);
|
|
437
|
+
if (ag) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-session-mark rm-session-agent", title: `DSH Room \xB7 ${r.name} \xB7 ${ag.name} (${ag.agentId})`, "aria-label": `DSH Room ${r.name} agent ${ag.name}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 13 }) });
|
|
438
|
+
}
|
|
439
|
+
const room = (s.rooms || []).find((r) => r.sessionId === sessionId);
|
|
440
|
+
if (!room) return null;
|
|
441
|
+
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 }) });
|
|
442
|
+
}
|
|
443
|
+
function WorkspaceSelect({ store, value, onChange, id }) {
|
|
444
|
+
const workspaces = useNativeWorkspaces(store);
|
|
445
|
+
(0, import_react.useEffect)(() => {
|
|
446
|
+
if (!value && workspaces[0]) onChange({ id: workspaces[0].workspaceId, path: workspaces[0].path, title: wsTitle(workspaces[0]) });
|
|
447
|
+
}, [workspaces.length]);
|
|
448
|
+
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)." });
|
|
449
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { id, className: "rm-input", value: value ? value.path : "", onChange: (e) => {
|
|
450
|
+
const w = workspaces.find((x) => x.path === e.target.value);
|
|
451
|
+
if (w) onChange({ id: w.workspaceId, path: w.path, title: wsTitle(w) });
|
|
452
|
+
}, children: workspaces.map((w) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", { value: w.path, children: [
|
|
453
|
+
wsTitle(w),
|
|
454
|
+
" \u2014 ",
|
|
455
|
+
w.path
|
|
456
|
+
] }, w.workspaceId)) });
|
|
457
|
+
}
|
|
458
|
+
function renderMentions(text) {
|
|
459
|
+
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);
|
|
460
|
+
}
|
|
461
|
+
function PanelIcon({ size }) {
|
|
462
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsIcon, { size });
|
|
463
|
+
}
|
|
464
|
+
function Message({ m, showAuthor, who }) {
|
|
497
465
|
if (m.kind === "task.create") {
|
|
498
466
|
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: [
|
|
499
467
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-task-head", children: [
|
|
500
468
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}),
|
|
501
469
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
502
470
|
"Task \xB7 ",
|
|
503
|
-
m.from
|
|
471
|
+
who && who.name || m.from
|
|
504
472
|
] }),
|
|
505
473
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "info", children: "open" }),
|
|
506
474
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-time", children: fmtTime(m.ts) })
|
|
@@ -513,7 +481,7 @@ function Message({ m, showAuthor }) {
|
|
|
513
481
|
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-head", children: [
|
|
514
482
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}),
|
|
515
483
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
516
|
-
m.self ? "You" : m.from,
|
|
484
|
+
m.self ? "You" : who && who.name || m.from,
|
|
517
485
|
" ",
|
|
518
486
|
m.kind === "task.claim" ? "claimed" : "updated",
|
|
519
487
|
" task"
|
|
@@ -533,14 +501,20 @@ function Message({ m, showAuthor }) {
|
|
|
533
501
|
showAuthor ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: m.from, size: 28 }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {}),
|
|
534
502
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
535
503
|
showAuthor ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-msg-head", children: [
|
|
536
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-author", children: m.from }),
|
|
504
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-author", children: who && who.name ? who.name : m.from }),
|
|
505
|
+
who && who.name ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-id", children: m.from }) : null,
|
|
506
|
+
who && who.local ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-tag", children: [
|
|
507
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 11 }),
|
|
508
|
+
who.moderator ? "moderator" : "local agent"
|
|
509
|
+
] }) : null,
|
|
537
510
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-time", children: fmtTime(m.ts) })
|
|
538
511
|
] }) : null,
|
|
539
512
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-msg-body", children: renderMentions(m.text) })
|
|
540
513
|
] })
|
|
541
514
|
] });
|
|
542
515
|
}
|
|
543
|
-
function ChatBody({ messages }) {
|
|
516
|
+
function ChatBody({ messages, members }) {
|
|
517
|
+
const byId = (0, import_react.useMemo)(() => Object.fromEntries((members || []).map((x) => [x.agentId, x])), [members]);
|
|
544
518
|
const ref = (0, import_react.useRef)(null);
|
|
545
519
|
(0, import_react.useEffect)(() => {
|
|
546
520
|
const el = ref.current;
|
|
@@ -556,7 +530,7 @@ function ChatBody({ messages }) {
|
|
|
556
530
|
prev = null;
|
|
557
531
|
}
|
|
558
532
|
const showAuthor = !prev || prev.from !== m.from || m.ts - prev.ts > 5 * 6e4;
|
|
559
|
-
nodes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Message, { m, showAuthor }, m.id));
|
|
533
|
+
nodes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Message, { m, showAuthor, who: byId[m.from] }, m.id));
|
|
560
534
|
prev = m;
|
|
561
535
|
}
|
|
562
536
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-messages", ref, children: nodes.length ? nodes : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-chat-hint", children: "No messages yet. Send a message, @mention an agent to wake it, or create a task from the composer." }) });
|
|
@@ -583,7 +557,7 @@ function MentionMenu({ items, active, onPick, onHover }) {
|
|
|
583
557
|
onPick(it);
|
|
584
558
|
},
|
|
585
559
|
children: [
|
|
586
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: it.id === "all" ? "all" : it.id, size: 22 }),
|
|
560
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: it.id === "all" ? "all" : it.name || it.id, size: 22 }),
|
|
587
561
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-mention-item-body", children: [
|
|
588
562
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-mention-item-name", children: [
|
|
589
563
|
"@",
|
|
@@ -591,6 +565,10 @@ function MentionMenu({ items, active, onPick, onHover }) {
|
|
|
591
565
|
] }),
|
|
592
566
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mention-item-meta", children: it.hint })
|
|
593
567
|
] }),
|
|
568
|
+
it.local ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-tag", children: [
|
|
569
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 11 }),
|
|
570
|
+
"local"
|
|
571
|
+
] }) : null,
|
|
594
572
|
it.online === false ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "quiet", children: "offline" }) : null
|
|
595
573
|
]
|
|
596
574
|
},
|
|
@@ -615,14 +593,20 @@ function Composer({ room, store, draft, setDraft }) {
|
|
|
615
593
|
if (!mq) return [];
|
|
616
594
|
const q = mq.query.toLowerCase();
|
|
617
595
|
const members = (room.members || []).filter((m) => !m.self);
|
|
618
|
-
const
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
596
|
+
const localAgents = room.local && room.local.agents || [];
|
|
597
|
+
const seen = /* @__PURE__ */ new Set();
|
|
598
|
+
const known = [];
|
|
599
|
+
for (const la of localAgents) {
|
|
600
|
+
seen.add(la.agentId);
|
|
601
|
+
known.push({ id: la.agentId, name: la.name, local: true, hint: `${la.name} \xB7 local agent${la.moderator ? " \xB7 moderator" : ""}${la.state && la.state !== "idle" ? ` \xB7 ${la.state}` : ""}`, online: la.online !== false });
|
|
602
|
+
}
|
|
603
|
+
for (const m of members) {
|
|
604
|
+
if (seen.has(m.agentId)) continue;
|
|
605
|
+
known.push({ id: m.agentId, name: m.name, hint: `${m.name ? m.name + " \xB7 " : ""}${m.hostedBy ? "remote agent" : "member"}${m.online === false ? " \xB7 offline" : " \xB7 wake"}`, online: m.online !== false });
|
|
606
|
+
}
|
|
607
|
+
const all = { id: "all", hint: localAgents.length ? "Everyone \xB7 wakes all local agents in parallel" : "Everyone \xB7 wake all members", online: true };
|
|
624
608
|
const list = [all, ...known];
|
|
625
|
-
return list.filter((it) => !q || it.id.toLowerCase().startsWith(q) ||
|
|
609
|
+
return list.filter((it) => !q || it.id.toLowerCase().startsWith(q) || it.name && it.name.toLowerCase().startsWith(q));
|
|
626
610
|
})();
|
|
627
611
|
const mc = room.memberConfig || {};
|
|
628
612
|
const modelLabel = mc.model || "Default model";
|
|
@@ -660,6 +644,23 @@ function Composer({ room, store, draft, setDraft }) {
|
|
|
660
644
|
a.toast(e.message);
|
|
661
645
|
}
|
|
662
646
|
};
|
|
647
|
+
const syncCaret = (el) => {
|
|
648
|
+
if (el) setCaret(el.selectionStart == null ? el.value.length : el.selectionStart);
|
|
649
|
+
};
|
|
650
|
+
const insertMention = (it) => {
|
|
651
|
+
if (!mq || !it) return;
|
|
652
|
+
const next = draft.slice(0, mq.start) + "@" + it.id + " " + draft.slice(mq.end);
|
|
653
|
+
const pos = mq.start + it.id.length + 2;
|
|
654
|
+
setDraft(next);
|
|
655
|
+
setCaret(pos);
|
|
656
|
+
requestAnimationFrame(() => {
|
|
657
|
+
const el = taRef.current;
|
|
658
|
+
if (el) {
|
|
659
|
+
el.focus();
|
|
660
|
+
el.setSelectionRange(pos, pos);
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
};
|
|
663
664
|
const submit = async () => {
|
|
664
665
|
const text = draft.trim();
|
|
665
666
|
if (!text || busy) return;
|
|
@@ -856,7 +857,15 @@ function RoomDetail({ room, messages, store, mention, assign }) {
|
|
|
856
857
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: m.agentId, size: 26 }),
|
|
857
858
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-dot rm-dot-" + (m.online === false ? "offline" : "online"), style: { position: "absolute", right: -1, bottom: -1, border: "2px solid var(--rm-bg)" } })
|
|
858
859
|
] }),
|
|
859
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-member-name", children: m.
|
|
860
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-member-name", children: m.name ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
861
|
+
m.name,
|
|
862
|
+
" ",
|
|
863
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-id", children: m.agentId })
|
|
864
|
+
] }) : m.agentId }),
|
|
865
|
+
m.local ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-tag", children: [
|
|
866
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 11 }),
|
|
867
|
+
m.moderator ? "moderator" : "local"
|
|
868
|
+
] }) : m.hostedBy && !m.self ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-agent-tag", children: "remote agent" }) : null,
|
|
860
869
|
m.self ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "quiet", children: "you" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-member-actions", children: [
|
|
861
870
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `@mention ${m.agentId}`, onClick: () => mention(m.agentId), children: "@" }),
|
|
862
871
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `Assign a task to ${m.agentId}`, onClick: () => assign(m.agentId), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}) })
|
|
@@ -864,6 +873,9 @@ function RoomDetail({ room, messages, store, mention, assign }) {
|
|
|
864
873
|
] }, m.agentId)),
|
|
865
874
|
!room.members.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: room.status === "online" ? "Only you are here." : "Offline." }) : null
|
|
866
875
|
] }),
|
|
876
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LocalAgentsSection, { room, store, mention }),
|
|
877
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(OrchestrationSection, { room, store }),
|
|
878
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(IsolationSection, { room, store }),
|
|
867
879
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", children: [
|
|
868
880
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
|
|
869
881
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}),
|
|
@@ -886,8 +898,10 @@ function RoomDetail({ room, messages, store, mention, assign }) {
|
|
|
886
898
|
"Invite"
|
|
887
899
|
] }),
|
|
888
900
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: room.public ? "Public: listed in the relay directory with its key. Anyone can find, join, and read this room." : "Private: only agents holding the invite can join. The invite contains the room key \u2014 share it only with agents you trust." }),
|
|
901
|
+
room.transport === "local" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Local-only room: switch Transport to Relay or LAN before inviting remote hosts." }) : null,
|
|
889
902
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: copyInvite, icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkIcon, {}), children: "Copy invite" }) })
|
|
890
903
|
] }),
|
|
904
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TransportSection, { room, store }),
|
|
891
905
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", children: [
|
|
892
906
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-detail-h", children: "About" }),
|
|
893
907
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dl", { className: "rm-about", children: [
|
|
@@ -900,7 +914,7 @@ function RoomDetail({ room, messages, store, mention, assign }) {
|
|
|
900
914
|
" Private"
|
|
901
915
|
] }) }),
|
|
902
916
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: "Transport" }),
|
|
903
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: room.transport === "lan" ? "LAN / VPS (Node relay)" : "Relay (Cloudflare)" }),
|
|
917
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: room.transport === "local" ? "Local only (in-process loopback)" : room.transport === "lan" ? "LAN / VPS (Node relay)" : "Relay (Cloudflare)" }),
|
|
904
918
|
room.memberConfig?.workspaceLabel ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
905
919
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: "Workspace" }),
|
|
906
920
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: room.memberConfig.workspaceLabel })
|
|
@@ -927,33 +941,392 @@ function RoomDetail({ room, messages, store, mention, assign }) {
|
|
|
927
941
|
] })
|
|
928
942
|
] });
|
|
929
943
|
}
|
|
930
|
-
|
|
944
|
+
var RUN_TONE = { running: "warning", queued: "info", approval: "info", done: "success", failed: "danger", stopped: "quiet" };
|
|
945
|
+
var MODEL_CHOICES = ["", "deepseek-chat", "deepseek-reasoner"];
|
|
946
|
+
function LocalAgentsSection({ room, store, mention }) {
|
|
947
|
+
const a = store.actions;
|
|
948
|
+
const local = room.local || { agents: [] };
|
|
949
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-local-agents": "", children: [
|
|
950
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
|
|
951
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, {}),
|
|
952
|
+
"Local agents \xB7 ",
|
|
953
|
+
local.agents.length
|
|
954
|
+
] }),
|
|
955
|
+
local.agents.map((ag) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-agent-row", "data-agent-id": ag.agentId, children: [
|
|
956
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-avatar-wrap", children: [
|
|
957
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: ag.name, size: 26 }),
|
|
958
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-dot rm-dot-" + (ag.state === "running" ? "busy" : ag.online ? "online" : "offline"), style: { position: "absolute", right: -1, bottom: -1, border: "2px solid var(--rm-bg)" } })
|
|
959
|
+
] }),
|
|
960
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-main", children: [
|
|
961
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-name", children: [
|
|
962
|
+
ag.name,
|
|
963
|
+
ag.moderator ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-agent-tag", children: "moderator" }) : null
|
|
964
|
+
] }),
|
|
965
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-sub rm-mono", children: [
|
|
966
|
+
"@",
|
|
967
|
+
ag.agentId
|
|
968
|
+
] }),
|
|
969
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-sub", children: [
|
|
970
|
+
ag.scope === "write" ? ag.isolation === "worktree" ? "writes \xB7 own worktree" : ag.isolation === "locks" ? "writes \xB7 file locks" : "writes" : "read-only",
|
|
971
|
+
ag.approvalRequired ? " \xB7 approval" : "",
|
|
972
|
+
ag.model ? ` \xB7 ${ag.model}` : "",
|
|
973
|
+
ag.reasoningEffort ? ` \xB7 ${ag.reasoningEffort}` : ""
|
|
974
|
+
] })
|
|
975
|
+
] }),
|
|
976
|
+
ag.state !== "idle" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: RUN_TONE[ag.state] || "neutral", children: ag.state }) : null,
|
|
977
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-member-actions", children: [
|
|
978
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `@mention ${ag.name}`, onClick: () => mention(ag.agentId), children: "@" }),
|
|
979
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `Edit ${ag.name}`, "aria-label": `Edit ${ag.name}`, onClick: () => a.openDialog({ kind: "agent", roomId: room.roomId, agentId: ag.agentId }), children: "\u270E" })
|
|
980
|
+
] })
|
|
981
|
+
] }, ag.agentId)),
|
|
982
|
+
!local.agents.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Host several agents from this dsh as separate members \u2014 each with its own identity, session and scope." }) : null,
|
|
983
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}), onClick: () => a.openDialog({ kind: "agent", roomId: room.roomId }), children: "Add local agent" }) })
|
|
984
|
+
] });
|
|
985
|
+
}
|
|
986
|
+
function NumberSetting({ label, value, min, max, onSave, hint }) {
|
|
987
|
+
const [v, setV] = (0, import_react.useState)(String(value));
|
|
988
|
+
(0, import_react.useEffect)(() => setV(String(value)), [value]);
|
|
989
|
+
const commit = () => {
|
|
990
|
+
const n = Number(v);
|
|
991
|
+
if (Number.isFinite(n) && n !== value) onSave(Math.min(max, Math.max(min, Math.round(n))));
|
|
992
|
+
else setV(String(value));
|
|
993
|
+
};
|
|
994
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "rm-num-setting", title: hint, children: [
|
|
995
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label }),
|
|
996
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "number", min, max, value: v, "aria-label": label, onChange: (e) => setV(e.target.value), onBlur: commit, onKeyDown: (e) => e.key === "Enter" && e.target.blur() })
|
|
997
|
+
] });
|
|
998
|
+
}
|
|
999
|
+
function RunQueue({ room, store, compact }) {
|
|
1000
|
+
const a = store.actions;
|
|
1001
|
+
const runs = room.runs || [];
|
|
1002
|
+
const nameOf = (id) => (room.local && room.local.agents || []).find((x) => x.agentId === id)?.name || id;
|
|
1003
|
+
const active = runs.filter((r) => r.state === "running" || r.state === "queued" || r.state === "approval");
|
|
1004
|
+
const recent = runs.filter((r) => !active.includes(r)).slice(-3).reverse();
|
|
1005
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-runs", "data-rm-runs": "", children: [
|
|
1006
|
+
active.map((r, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-run rm-run-" + r.state, children: [
|
|
1007
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-pos", children: r.state === "running" ? "\u25B6" : r.state === "approval" ? "?" : `#${i + 1 - active.filter((x) => x.state === "running").length}` }),
|
|
1008
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-who", children: nameOf(r.agentId) }),
|
|
1009
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-what", title: r.summary, children: r.summary }),
|
|
1010
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: RUN_TONE[r.state], children: r.state === "approval" ? "needs approval" : r.state })
|
|
1011
|
+
] }, r.id)),
|
|
1012
|
+
!compact ? recent.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-run rm-run-" + r.state, children: [
|
|
1013
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-pos", children: "\u2713" }),
|
|
1014
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-who", children: nameOf(r.agentId) }),
|
|
1015
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-what", title: r.error || r.summary, children: r.error || r.summary }),
|
|
1016
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: RUN_TONE[r.state], children: r.state })
|
|
1017
|
+
] }, r.id)) : null,
|
|
1018
|
+
!active.length && !recent.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "No agent runs yet." }) : null,
|
|
1019
|
+
active.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", className: "rm-danger", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StopIcon, {}), onClick: () => a.stop(room.roomId).then((r) => a.toast(`Stopped ${r.stopped} run(s)`)).catch((e) => a.toast(e.message)), children: "Stop all agents" }) }) : null
|
|
1020
|
+
] });
|
|
1021
|
+
}
|
|
1022
|
+
function OrchestrationSection({ room, store }) {
|
|
1023
|
+
const a = store.actions;
|
|
1024
|
+
const local = room.local;
|
|
1025
|
+
if (!local || !local.agents.length) return null;
|
|
1026
|
+
const save = (patch) => a.saveLocal(room.roomId, patch).catch((e) => a.toast(e.message));
|
|
1027
|
+
const active = (room.runs || []).filter((r) => r.state === "running" || r.state === "queued" || r.state === "approval");
|
|
1028
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-orchestration": "", children: [
|
|
1029
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
|
|
1030
|
+
"Runs \xB7 ",
|
|
1031
|
+
active.filter((r) => r.state === "running").length,
|
|
1032
|
+
"/",
|
|
1033
|
+
local.concurrency,
|
|
1034
|
+
" running",
|
|
1035
|
+
active.length > local.concurrency ? ` \xB7 ${active.length - Math.min(local.concurrency, active.filter((r) => r.state === "running").length)} queued` : ""
|
|
1036
|
+
] }),
|
|
1037
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-num-row", children: [
|
|
1038
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(NumberSetting, { label: "Parallel runs", value: local.concurrency, min: 1, max: 16, onSave: (n) => save({ concurrency: n }), hint: "Max local agent runs at once; the rest wait in the queue." }),
|
|
1039
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(NumberSetting, { label: "Turn limit", value: local.turnLimit, min: 1, max: 100, onSave: (n) => save({ turnLimit: n }), hint: "Max agent-to-agent hand-offs per task/thread. A human message resets it." })
|
|
1040
|
+
] }),
|
|
1041
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RunQueue, { room, store })
|
|
1042
|
+
] });
|
|
1043
|
+
}
|
|
1044
|
+
function IsolationSection({ room, store }) {
|
|
1045
|
+
const a = store.actions;
|
|
1046
|
+
const local = room.local;
|
|
1047
|
+
if (!local || !local.agents.some((x) => x.scope === "write")) return null;
|
|
1048
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-isolation": "", children: [
|
|
1049
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
|
|
1050
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeIcon, {}),
|
|
1051
|
+
"Workspace isolation"
|
|
1052
|
+
] }),
|
|
1053
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { className: "rm-input", "aria-label": "Isolation mode", value: local.isolation, onChange: (e) => a.saveLocal(room.roomId, { isolation: e.target.value }).catch((x) => a.toast(x.message)), children: [
|
|
1054
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "auto", children: "Auto \u2014 git worktree per writing agent, else file locks" }),
|
|
1055
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "worktree", children: "Git worktree per agent" }),
|
|
1056
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "locks", children: "File locks (shared workspace)" }),
|
|
1057
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "off", children: "Off (shared, no locks)" })
|
|
1058
|
+
] }),
|
|
1059
|
+
local.locks && local.locks.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-note", children: [
|
|
1060
|
+
local.locks.length,
|
|
1061
|
+
" file lock(s): ",
|
|
1062
|
+
local.locks.slice(0, 4).map((l) => `${l.path} (${l.owner})`).join(", ")
|
|
1063
|
+
] }) : null,
|
|
1064
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeIcon, {}), onClick: () => a.openDialog({ kind: "merge", roomId: room.roomId }), children: "Review & merge" }) })
|
|
1065
|
+
] });
|
|
1066
|
+
}
|
|
1067
|
+
function TransportSection({ room, store }) {
|
|
1068
|
+
const a = store.actions;
|
|
1069
|
+
const [busy, setBusy] = (0, import_react.useState)(null);
|
|
1070
|
+
const cur = room.transport === "local" ? "local" : room.transport === "lan" ? "relay" : "relay";
|
|
1071
|
+
const go = async (target) => {
|
|
1072
|
+
setBusy(target);
|
|
1073
|
+
try {
|
|
1074
|
+
await a.setTransport(room.roomId, target);
|
|
1075
|
+
a.toast(target === "local" ? "Local only \u2014 in-process, no network" : target === "lan" ? "Now on this machine's LAN relay \u2014 invite remote hosts" : "Now on the relay \u2014 invite remote hosts");
|
|
1076
|
+
} catch (e) {
|
|
1077
|
+
a.toast(e.message);
|
|
1078
|
+
} finally {
|
|
1079
|
+
setBusy(null);
|
|
1080
|
+
}
|
|
1081
|
+
};
|
|
1082
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-transport": "", children: [
|
|
1083
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-detail-h", children: "Transport" }),
|
|
1084
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
|
|
1085
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: !!busy || room.public, className: "rm-mode-btn" + (cur === "local" ? " is-on" : ""), onClick: () => go("local"), title: room.public ? "Public rooms need a relay" : "In-process loopback: no network", children: busy === "local" ? "\u2026" : "Local only" }),
|
|
1086
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: !!busy, className: "rm-mode-btn" + (cur === "relay" && room.transport !== "local" ? " is-on" : ""), onClick: () => go("relay"), children: busy === "relay" ? "\u2026" : "Relay (VPS)" }),
|
|
1087
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: !!busy, className: "rm-mode-btn", onClick: () => go("lan"), title: "Embedded LAN relay on this machine (mDNS)", children: busy === "lan" ? "\u2026" : "LAN" })
|
|
1088
|
+
] }),
|
|
1089
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: room.transport === "local" ? "All members are on this dsh \u2014 nothing leaves the process. Switch to Relay or LAN to invite remote hosts; local agents stay members." : "Local agents connect as separate members over this transport, alongside remote hosts." })
|
|
1090
|
+
] });
|
|
1091
|
+
}
|
|
1092
|
+
function AgentDialog({ store, d }) {
|
|
1093
|
+
const a = store.actions;
|
|
931
1094
|
const s = useStore(store);
|
|
1095
|
+
const room = (s.rooms || []).find((r) => r.roomId === d.roomId);
|
|
1096
|
+
const existing = room && room.local ? room.local.agents.find((x) => x.agentId === d.agentId) : null;
|
|
1097
|
+
const [presets, setPresets] = (0, import_react.useState)([]);
|
|
1098
|
+
const [f, setF] = (0, import_react.useState)(() => existing ? { name: existing.name, persona: existing.persona || "", systemPrompt: existing.systemPrompt || "", model: existing.model || "", reasoningEffort: existing.reasoningEffort || "medium", scope: existing.scope, approvalRequired: !!existing.approvalRequired, moderator: !!existing.moderator } : { preset: "", name: "", persona: "", systemPrompt: "", model: "", reasoningEffort: "medium", scope: "read", approvalRequired: false, moderator: false });
|
|
1099
|
+
const [err, setErr] = (0, import_react.useState)(null);
|
|
1100
|
+
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
1101
|
+
(0, import_react.useEffect)(() => {
|
|
1102
|
+
a.presets().then(setPresets).catch(() => {
|
|
1103
|
+
});
|
|
1104
|
+
}, []);
|
|
1105
|
+
const pick = (p) => setF({ ...f, preset: p.key, name: p.name, persona: p.persona, systemPrompt: p.systemPrompt, scope: p.scope, moderator: !!p.moderator, reasoningEffort: p.reasoningEffort || "medium" });
|
|
1106
|
+
const submit = async () => {
|
|
1107
|
+
if (!f.name.trim()) {
|
|
1108
|
+
setErr("Name required");
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
setBusy(true);
|
|
1112
|
+
setErr(null);
|
|
1113
|
+
try {
|
|
1114
|
+
const body = { name: f.name, persona: f.persona, systemPrompt: f.systemPrompt, model: f.model, reasoningEffort: f.reasoningEffort, scope: f.scope, approvalRequired: f.approvalRequired, moderator: f.moderator, preset: f.preset || void 0 };
|
|
1115
|
+
if (existing) await a.updateAgent(d.roomId, existing.agentId, body);
|
|
1116
|
+
else {
|
|
1117
|
+
const r = await a.addAgent(d.roomId, body);
|
|
1118
|
+
a.toast(`${r.name} joined as @${r.agentId}`);
|
|
1119
|
+
}
|
|
1120
|
+
a.closeDialog();
|
|
1121
|
+
} catch (e) {
|
|
1122
|
+
setErr(e.message);
|
|
1123
|
+
setBusy(false);
|
|
1124
|
+
}
|
|
1125
|
+
};
|
|
1126
|
+
const remove = async () => {
|
|
1127
|
+
setBusy(true);
|
|
1128
|
+
try {
|
|
1129
|
+
const r = await a.removeAgent(d.roomId, existing.agentId);
|
|
1130
|
+
a.toast(`Removed ${existing.name}${r.branchKept ? ` \xB7 branch ${r.branchKept} kept` : ""}`);
|
|
1131
|
+
a.closeDialog();
|
|
1132
|
+
} catch (e) {
|
|
1133
|
+
setErr(e.message);
|
|
1134
|
+
setBusy(false);
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1138
|
+
import_dsh_client_ui_primitives.Modal,
|
|
1139
|
+
{
|
|
1140
|
+
open: true,
|
|
1141
|
+
onClose: a.closeDialog,
|
|
1142
|
+
title: existing ? `Edit ${existing.name}` : "Add local agent",
|
|
1143
|
+
closeLabel: "Close",
|
|
1144
|
+
className: "rm-modal-wide",
|
|
1145
|
+
description: existing ? `Member @${existing.agentId} \xB7 own identity and dsh session in this room's workspace.` : "Runs on this dsh as a separate room member: own keypair, own session, own scope. Remote peers see it as a member.",
|
|
1146
|
+
footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1147
|
+
existing ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", className: "rm-danger", disabled: busy, onClick: remove, children: "Remove" }) : null,
|
|
1148
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { flex: 1 } }),
|
|
1149
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
|
|
1150
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: busy, onClick: submit, children: busy ? "Saving\u2026" : existing ? "Save" : "Add agent" })
|
|
1151
|
+
] }),
|
|
1152
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form rm-form-wide", "data-rm-agent-form": "", children: [
|
|
1153
|
+
!existing ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2", children: [
|
|
1154
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Preset" }),
|
|
1155
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-preset-row", children: presets.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-preset" + (f.preset === p.key ? " is-on" : ""), onClick: () => pick(p), children: [
|
|
1156
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: p.name }),
|
|
1157
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: p.persona })
|
|
1158
|
+
] }, p.key)) })
|
|
1159
|
+
] }) : null,
|
|
1160
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Name", htmlFor: "rm-ag-name", hint: "Member id is derived from it (e.g. coder.ab12).", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-ag-name", value: f.name, maxLength: 40, onChange: (e) => setF({ ...f, name: e.target.value }), placeholder: "e.g. Coder" }) }),
|
|
1161
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Persona", htmlFor: "rm-ag-persona", hint: "One line other members see.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-ag-persona", value: f.persona, maxLength: 200, onChange: (e) => setF({ ...f, persona: e.target.value }), placeholder: "e.g. Implements changes" }) }),
|
|
1162
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "System prompt", htmlFor: "rm-ag-sp", hint: "Prepended to every wake of this agent.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { id: "rm-ag-sp", className: "rm-input rm-textarea", rows: 4, value: f.systemPrompt, maxLength: 4e3, onChange: (e) => setF({ ...f, systemPrompt: e.target.value }) }) }) }),
|
|
1163
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Model", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { className: "rm-input", "aria-label": "Model", value: f.model, onChange: (e) => setF({ ...f, model: e.target.value }), children: [
|
|
1164
|
+
MODEL_CHOICES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: m, children: m || "dsh default model" }, m)),
|
|
1165
|
+
f.model && !MODEL_CHOICES.includes(f.model) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: f.model, children: f.model }) : null
|
|
1166
|
+
] }) }),
|
|
1167
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Reasoning effort", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { className: "rm-input", "aria-label": "Reasoning effort", value: f.reasoningEffort, onChange: (e) => setF({ ...f, reasoningEffort: e.target.value }), children: ["low", "medium", "high", "max"].map((x) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: x, children: x }, x)) }) }),
|
|
1168
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Workspace scope", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
|
|
1169
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.scope === "read" ? " is-on" : ""), onClick: () => setF({ ...f, scope: "read" }), children: "Read-only" }),
|
|
1170
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.scope === "write" ? " is-on" : ""), onClick: () => setF({ ...f, scope: "write" }), children: "Read + write" })
|
|
1171
|
+
] }) }),
|
|
1172
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-col", children: [
|
|
1173
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
|
|
1174
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1175
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Approval required" }),
|
|
1176
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Each run waits for your OK." })
|
|
1177
|
+
] }),
|
|
1178
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.approvalRequired, onChange: (v) => setF({ ...f, approvalRequired: v }), label: "Approval required" })
|
|
1179
|
+
] }),
|
|
1180
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
|
|
1181
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1182
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Moderator" }),
|
|
1183
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Splits requests into tasks and summarizes. One per room." })
|
|
1184
|
+
] }),
|
|
1185
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.moderator, onChange: (v) => setF({ ...f, moderator: v }), label: "Moderator" })
|
|
1186
|
+
] })
|
|
1187
|
+
] }),
|
|
1188
|
+
f.scope === "write" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-note", children: "Writes go to its own git worktree (branch room/<room>/<agent>) when the workspace is a git repo; you merge from \u201CReview & merge\u201D. Non-git workspaces use file locks." }) : null,
|
|
1189
|
+
err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-error", children: err }) : null
|
|
1190
|
+
] })
|
|
1191
|
+
}
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
function DiffView({ patch }) {
|
|
1195
|
+
if (!patch) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "No changes." });
|
|
1196
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: "rm-diff", "data-rm-diff": "", children: patch.split("\n").map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: l.startsWith("+++") || l.startsWith("---") ? "rm-diff-file" : l.startsWith("+") ? "rm-diff-add" : l.startsWith("-") ? "rm-diff-del" : l.startsWith("@@") ? "rm-diff-hunk" : l.startsWith("diff ") ? "rm-diff-head" : "", children: l || " " }, i)) });
|
|
1197
|
+
}
|
|
1198
|
+
function MergeDialog({ store, d }) {
|
|
1199
|
+
const a = store.actions;
|
|
1200
|
+
const [data, setData] = (0, import_react.useState)(null);
|
|
1201
|
+
const [sel, setSel] = (0, import_react.useState)(null);
|
|
1202
|
+
const [confirm, setConfirm] = (0, import_react.useState)(null);
|
|
1203
|
+
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
1204
|
+
const [result, setResult] = (0, import_react.useState)(null);
|
|
1205
|
+
const load = async () => {
|
|
1206
|
+
try {
|
|
1207
|
+
const r = await a.diffs(d.roomId);
|
|
1208
|
+
setData(r);
|
|
1209
|
+
setSel((cur) => cur && r.agents.some((x) => x.agentId === cur) ? cur : (r.agents.find((x) => x.ahead) || r.agents[0] || {}).agentId || null);
|
|
1210
|
+
} catch (e) {
|
|
1211
|
+
setData({ error: e.message, agents: [] });
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
(0, import_react.useEffect)(() => {
|
|
1215
|
+
void load();
|
|
1216
|
+
}, [d.roomId]);
|
|
1217
|
+
const ag = data && data.agents.find((x) => x.agentId === sel);
|
|
1218
|
+
const run = async (kind) => {
|
|
1219
|
+
setBusy(true);
|
|
1220
|
+
setResult(null);
|
|
1221
|
+
try {
|
|
1222
|
+
const r = kind === "merge" ? await a.merge(d.roomId, sel) : await a.discard(d.roomId, sel);
|
|
1223
|
+
setResult(kind === "merge" ? r.merged ? { ok: true, text: `Merged into the main workspace (${r.commit}).` } : { ok: false, text: `${r.error || "Not merged"}${r.conflicts && r.conflicts.length ? ": " + r.conflicts.join(", ") : ""}` } : { ok: true, text: r.discarded ? `Discarded ${r.branch}.` : "Nothing to discard." });
|
|
1224
|
+
await load();
|
|
1225
|
+
} catch (e) {
|
|
1226
|
+
setResult({ ok: false, text: e.message });
|
|
1227
|
+
} finally {
|
|
1228
|
+
setBusy(false);
|
|
1229
|
+
setConfirm(null);
|
|
1230
|
+
}
|
|
1231
|
+
};
|
|
1232
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1233
|
+
import_dsh_client_ui_primitives.Modal,
|
|
1234
|
+
{
|
|
1235
|
+
open: true,
|
|
1236
|
+
onClose: a.closeDialog,
|
|
1237
|
+
title: "Review & merge agent changes",
|
|
1238
|
+
closeLabel: "Close",
|
|
1239
|
+
className: "rm-modal-wide rm-modal-xl",
|
|
1240
|
+
description: "Each writing agent works on its own branch. Review the diff, then merge into (or discard from) your main workspace. Nothing is merged without your confirmation.",
|
|
1241
|
+
footer: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Close" }),
|
|
1242
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-root rm-merge", "data-rm-merge": "", children: !data ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Loading diffs\u2026" }) : data.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: data.error }) : !data.agents.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "No writing agents in this room." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1243
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-merge-list", children: data.agents.map((x) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-merge-agent" + (x.agentId === sel ? " is-on" : ""), onClick: () => {
|
|
1244
|
+
setSel(x.agentId);
|
|
1245
|
+
setConfirm(null);
|
|
1246
|
+
setResult(null);
|
|
1247
|
+
}, children: [
|
|
1248
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: x.name, size: 24 }),
|
|
1249
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-merge-agent-body", children: [
|
|
1250
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: x.name }),
|
|
1251
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: x.branch || (x.isolation === "locks" ? "shared workspace \xB7 locks" : "no worktree yet") })
|
|
1252
|
+
] }),
|
|
1253
|
+
x.conflicts && x.conflicts.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "danger", children: "conflict" }) : x.ahead ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_dsh_client_ui_primitives.Tag, { tone: "info", children: [
|
|
1254
|
+
x.files.length,
|
|
1255
|
+
" file",
|
|
1256
|
+
x.files.length === 1 ? "" : "s"
|
|
1257
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "quiet", children: "clean" })
|
|
1258
|
+
] }, x.agentId)) }),
|
|
1259
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-merge-main", children: ag ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1260
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-merge-head", children: [
|
|
1261
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1262
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: ag.name }),
|
|
1263
|
+
" ",
|
|
1264
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: ag.branch || "" }),
|
|
1265
|
+
ag.ahead ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-note", style: { margin: "0 0 0 8px" }, children: [
|
|
1266
|
+
ag.ahead,
|
|
1267
|
+
" commit(s) ahead"
|
|
1268
|
+
] }) : null
|
|
1269
|
+
] }),
|
|
1270
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", style: { margin: 0 }, children: confirm ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1271
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-confirm-text", children: confirm === "merge" ? `Merge ${ag.files.length} file(s) into the main workspace?` : `Discard ${ag.name}'s branch and worktree?` }),
|
|
1272
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: () => setConfirm(null), children: "Cancel" }),
|
|
1273
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "primary", disabled: busy, onClick: () => run(confirm), children: busy ? "Working\u2026" : confirm === "merge" ? "Confirm merge" : "Confirm discard" })
|
|
1274
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1275
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "ghost", className: "rm-danger", disabled: !ag.branch || busy, onClick: () => setConfirm("discard"), children: "Discard" }),
|
|
1276
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeIcon, {}), disabled: !ag.ahead || busy, onClick: () => setConfirm("merge"), children: "Merge into main" })
|
|
1277
|
+
] }) })
|
|
1278
|
+
] }),
|
|
1279
|
+
result ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: result.ok ? "rm-ok" : "rm-error", children: result.text }) : null,
|
|
1280
|
+
ag.conflicts && ag.conflicts.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-error", children: [
|
|
1281
|
+
"Would conflict with the main workspace: ",
|
|
1282
|
+
ag.conflicts.join(", "),
|
|
1283
|
+
" \u2014 resolve on the branch or discard."
|
|
1284
|
+
] }) : null,
|
|
1285
|
+
ag.mainDirty && ag.mainDirty.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-error", children: [
|
|
1286
|
+
"Uncommitted changes in the main workspace touch: ",
|
|
1287
|
+
ag.mainDirty.join(", ")
|
|
1288
|
+
] }) : null,
|
|
1289
|
+
ag.files && ag.files.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-merge-files", children: ag.files.map((f) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-merge-file", children: [
|
|
1290
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: f.path }),
|
|
1291
|
+
" ",
|
|
1292
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-diff-addn", children: [
|
|
1293
|
+
"+",
|
|
1294
|
+
f.adds
|
|
1295
|
+
] }),
|
|
1296
|
+
" ",
|
|
1297
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-diff-deln", children: [
|
|
1298
|
+
"\u2212",
|
|
1299
|
+
f.dels
|
|
1300
|
+
] })
|
|
1301
|
+
] }, f.path)) }) : null,
|
|
1302
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DiffView, { patch: ag.patch }),
|
|
1303
|
+
ag.truncated ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Diff truncated." }) : null
|
|
1304
|
+
] }) : null })
|
|
1305
|
+
] }) })
|
|
1306
|
+
}
|
|
1307
|
+
);
|
|
1308
|
+
}
|
|
1309
|
+
function EmptyMain({ store }) {
|
|
932
1310
|
const a = store.actions;
|
|
933
|
-
|
|
1311
|
+
const workspaces = useNativeWorkspaces(store);
|
|
1312
|
+
const first = workspaces[0];
|
|
1313
|
+
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
1314
|
/* @__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: "
|
|
1315
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: "Rooms live in your workspaces" }),
|
|
1316
|
+
/* @__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
1317
|
/* @__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: [
|
|
1318
|
+
/* @__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
1319
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}),
|
|
947
1320
|
/* @__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:
|
|
1321
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "New room" }),
|
|
1322
|
+
/* @__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
1323
|
] })
|
|
951
1324
|
] }),
|
|
952
1325
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "join" }), children: [
|
|
953
1326
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkIcon, {}),
|
|
954
1327
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
955
1328
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Join by invite" }),
|
|
956
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Paste a collab
|
|
1329
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Paste a collab://\u2026 invite" })
|
|
957
1330
|
] })
|
|
958
1331
|
] }),
|
|
959
1332
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "browse" }), children: [
|
|
@@ -970,51 +1343,74 @@ function RoomsMain({ store }) {
|
|
|
970
1343
|
const s = useStore(store);
|
|
971
1344
|
const [drafts, setDrafts] = (0, import_react.useState)({});
|
|
972
1345
|
const room = s.rooms.find((r) => r.roomId === s.selected);
|
|
973
|
-
if (!room) return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
1346
|
+
if (!room) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-main", "data-collab-main": "", children: [
|
|
1347
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsNav, { store }),
|
|
1348
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-main-body", children: [
|
|
1349
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApprovalBanner, { store }),
|
|
1350
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyMain, { store })
|
|
1351
|
+
] })
|
|
1352
|
+
] });
|
|
974
1353
|
const messages = s.messages[room.roomId] || [];
|
|
975
1354
|
const draft = drafts[room.roomId] || "";
|
|
976
1355
|
const setDraft = (v) => setDrafts((d) => ({ ...d, [room.roomId]: typeof v === "function" ? v(d[room.roomId] || "") : v }));
|
|
977
1356
|
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
|
-
|
|
1357
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsNav, { store }),
|
|
1358
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-main-body", children: [
|
|
1359
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApprovalBanner, { store }),
|
|
1360
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-workspace", children: [
|
|
1361
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "rm-chat", children: [
|
|
1362
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "rm-chat-header", children: [
|
|
1363
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: room.name, size: 28 }),
|
|
1364
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
1365
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-chat-header-title", children: room.name }),
|
|
1366
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-chat-header-sub", children: [
|
|
1367
|
+
room.members.filter((m) => m.online !== false).length,
|
|
1368
|
+
" online",
|
|
1369
|
+
room.local && room.local.agents.length ? ` \xB7 ${room.local.agents.length} local agent${room.local.agents.length === 1 ? "" : "s"}` : "",
|
|
1370
|
+
" \xB7 ",
|
|
1371
|
+
room.public ? "public" : "private",
|
|
1372
|
+
room.status !== "online" ? ` \xB7 room ${room.status}` : ""
|
|
1373
|
+
] })
|
|
1374
|
+
] }),
|
|
1375
|
+
(() => {
|
|
1376
|
+
const act = (room.runs || []).filter((r) => r.state === "running" || r.state === "queued" || r.state === "approval");
|
|
1377
|
+
if (!act.length) return null;
|
|
1378
|
+
const running = act.filter((r) => r.state === "running").length;
|
|
1379
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-stop-btn", "data-rm-stop": "", title: "Stop all local agent runs", onClick: () => store.actions.stop(room.roomId).then((r) => store.actions.toast(`Stopped ${r.stopped} run(s)`)).catch((e) => store.actions.toast(e.message)), children: [
|
|
1380
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StopIcon, {}),
|
|
1381
|
+
"Stop \xB7 ",
|
|
1382
|
+
running,
|
|
1383
|
+
" running",
|
|
1384
|
+
act.length > running ? `, ${act.length - running} queued` : ""
|
|
1385
|
+
] });
|
|
1386
|
+
})(),
|
|
1387
|
+
room.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge rm-badge-public", children: [
|
|
1388
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
|
|
1389
|
+
"Public"
|
|
1390
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge", children: [
|
|
1391
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}),
|
|
1392
|
+
"Private"
|
|
1393
|
+
] }),
|
|
1394
|
+
room.transport === "local" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge rm-badge-local", children: "Local only" }) : room.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
|
|
1395
|
+
/* @__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
1396
|
] }),
|
|
999
|
-
|
|
1000
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1397
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages, members: room.members }),
|
|
1398
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Composer, { room, store, draft, setDraft })
|
|
1001
1399
|
] }),
|
|
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}`);
|
|
1400
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1401
|
+
RoomDetail,
|
|
1402
|
+
{
|
|
1403
|
+
room,
|
|
1404
|
+
messages,
|
|
1405
|
+
store,
|
|
1406
|
+
mention: (id) => setDraft((d) => `${d}${d && !d.endsWith(" ") ? " " : ""}@${id} `),
|
|
1407
|
+
assign: (id) => {
|
|
1408
|
+
setDraft(`@${id} `);
|
|
1409
|
+
store.actions.toast(`Switch to task mode, then describe the task for @${id}`);
|
|
1410
|
+
}
|
|
1015
1411
|
}
|
|
1016
|
-
|
|
1017
|
-
)
|
|
1412
|
+
)
|
|
1413
|
+
] })
|
|
1018
1414
|
] })
|
|
1019
1415
|
] });
|
|
1020
1416
|
}
|
|
@@ -1025,17 +1421,21 @@ function Field({ label, hint, children, htmlFor }) {
|
|
|
1025
1421
|
hint ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: hint }) : null
|
|
1026
1422
|
] });
|
|
1027
1423
|
}
|
|
1028
|
-
function CreateDialog({ store }) {
|
|
1424
|
+
function CreateDialog({ store, d }) {
|
|
1029
1425
|
const s = useStore(store);
|
|
1030
1426
|
const a = store.actions;
|
|
1031
|
-
const [f, setF] = (0, import_react.useState)({ name: "", description: "", tags: "", public: false, mode: "lan", lanHostPort: "62.234.113.56:80" });
|
|
1427
|
+
const [f, setF] = (0, import_react.useState)({ name: "", description: "", tags: "", public: false, mode: "lan", lanHostPort: "62.234.113.56:80", localOnly: false, agents: [] });
|
|
1032
1428
|
const [err, setErr] = (0, import_react.useState)(null);
|
|
1033
1429
|
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
1034
1430
|
const [lanPeers, setLanPeers] = (0, import_react.useState)([]);
|
|
1035
1431
|
const ok = f.name.trim() && !busy;
|
|
1036
|
-
const ws = (
|
|
1432
|
+
const [ws, setWs] = (0, import_react.useState)(d && d.workspace ? d.workspace : null);
|
|
1037
1433
|
const submit = async () => {
|
|
1038
1434
|
if (!ok) return;
|
|
1435
|
+
if (!ws) {
|
|
1436
|
+
setErr("Pick a workspace");
|
|
1437
|
+
return;
|
|
1438
|
+
}
|
|
1039
1439
|
setBusy(true);
|
|
1040
1440
|
setErr(null);
|
|
1041
1441
|
try {
|
|
@@ -1045,7 +1445,11 @@ function CreateDialog({ store }) {
|
|
|
1045
1445
|
tags: f.tags,
|
|
1046
1446
|
public: f.public,
|
|
1047
1447
|
mode: f.mode,
|
|
1048
|
-
lanHostPort: f.mode === "lan" ? f.lanHostPort.trim() : void 0
|
|
1448
|
+
lanHostPort: f.mode === "lan" ? f.lanHostPort.trim() : void 0,
|
|
1449
|
+
workspacePath: ws.path,
|
|
1450
|
+
workspaceName: ws.title,
|
|
1451
|
+
localOnly: f.localOnly,
|
|
1452
|
+
agents: f.agents
|
|
1049
1453
|
});
|
|
1050
1454
|
} catch (e) {
|
|
1051
1455
|
setErr(e.message);
|
|
@@ -1068,12 +1472,13 @@ function CreateDialog({ store }) {
|
|
|
1068
1472
|
title: "Create room",
|
|
1069
1473
|
closeLabel: "Close",
|
|
1070
1474
|
className: "rm-modal-wide",
|
|
1071
|
-
description: ws ? `In workspace \u201C${ws.
|
|
1475
|
+
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
1476
|
footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1073
1477
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
|
|
1074
1478
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: !ok, onClick: submit, children: busy ? "Creating\u2026" : "Create" })
|
|
1075
1479
|
] }),
|
|
1076
1480
|
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: [
|
|
1481
|
+
/* @__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
1482
|
/* @__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
1483
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: f.name || "room", size: 36 }),
|
|
1079
1484
|
/* @__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 }) })
|
|
@@ -1094,12 +1499,29 @@ function CreateDialog({ store }) {
|
|
|
1094
1499
|
] }, p.hostPort))
|
|
1095
1500
|
] })
|
|
1096
1501
|
] }) }) : null,
|
|
1502
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-local-box", children: [
|
|
1503
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-switch-row", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1504
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Local agents" }),
|
|
1505
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Host several agents from this dsh as separate members (own identity, session, scope). Edit them later in room details." })
|
|
1506
|
+
] }) }),
|
|
1507
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-preset-row", children: [["moderator", "Moderator"], ["architect", "Architect"], ["coder", "Coder"], ["reviewer", "Reviewer"]].map(([k, label]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "rm-preset rm-preset-check" + (f.agents.includes(k) ? " is-on" : ""), children: [
|
|
1508
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: f.agents.includes(k), onChange: (e) => setF({ ...f, agents: e.target.checked ? [...f.agents, k] : f.agents.filter((x) => x !== k) }) }),
|
|
1509
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: label })
|
|
1510
|
+
] }, k)) }),
|
|
1511
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
|
|
1512
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1513
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Local only (no network)" }),
|
|
1514
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "In-process loopback \u2014 nothing leaves this machine. One click to switch to Relay/LAN later." })
|
|
1515
|
+
] }),
|
|
1516
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.localOnly, onChange: (v) => setF({ ...f, localOnly: v, public: v ? false : f.public }), label: "Local only" })
|
|
1517
|
+
] })
|
|
1518
|
+
] }),
|
|
1097
1519
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-switch-row", children: [
|
|
1098
1520
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1099
1521
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "List in public directory" }),
|
|
1100
1522
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: f.public ? "Anyone can find and join \u2014 key is published with the listing." : "Private: invite only." })
|
|
1101
1523
|
] }),
|
|
1102
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.public, onChange: (v) => setF({ ...f, public: v }), label: "List in public directory" })
|
|
1524
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.public, onChange: (v) => setF({ ...f, public: v, localOnly: v ? false : f.localOnly }), label: "List in public directory" })
|
|
1103
1525
|
] }),
|
|
1104
1526
|
err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-error", children: err }) : null
|
|
1105
1527
|
] })
|
|
@@ -1113,12 +1535,13 @@ function JoinDialog({ store }) {
|
|
|
1113
1535
|
const [err, setErr] = (0, import_react.useState)(null);
|
|
1114
1536
|
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
1115
1537
|
const valid = /^collab:\/\/(join|lan|relay)\/.+#\S+$/.test(invite.trim());
|
|
1538
|
+
const [ws, setWs] = (0, import_react.useState)(null);
|
|
1116
1539
|
const submit = async () => {
|
|
1117
1540
|
if (!valid || busy) return;
|
|
1118
1541
|
setBusy(true);
|
|
1119
1542
|
setErr(null);
|
|
1120
1543
|
try {
|
|
1121
|
-
await a.join(invite.trim());
|
|
1544
|
+
await a.join(invite.trim(), ws ? { workspacePath: ws.path, workspaceName: ws.title } : void 0);
|
|
1122
1545
|
} catch (e) {
|
|
1123
1546
|
setErr(e.message);
|
|
1124
1547
|
setBusy(false);
|
|
@@ -1131,12 +1554,13 @@ function JoinDialog({ store }) {
|
|
|
1131
1554
|
onClose: a.closeDialog,
|
|
1132
1555
|
title: "Join room",
|
|
1133
1556
|
closeLabel: "Close",
|
|
1134
|
-
description: "Paste an invite
|
|
1557
|
+
description: "Paste an invite and pick the dsh workspace (your checkout of the same project) the room attaches to.",
|
|
1135
1558
|
footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1136
1559
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
|
|
1137
1560
|
/* @__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
1561
|
] }),
|
|
1139
1562
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form", children: [
|
|
1563
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceSelect, { store, value: ws, onChange: setWs }),
|
|
1140
1564
|
/* @__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
1565
|
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
1566
|
err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: err }) : null
|
|
@@ -1326,9 +1750,10 @@ function WorkspaceDialog({ store }) {
|
|
|
1326
1750
|
setBusy(true);
|
|
1327
1751
|
setErr(null);
|
|
1328
1752
|
try {
|
|
1329
|
-
|
|
1753
|
+
if (store.native && store.native.create) await store.native.create({ path: path.trim() });
|
|
1754
|
+
else await a.ensureWorkspace({ name: name2.trim() || void 0, path: path.trim() });
|
|
1330
1755
|
a.closeDialog();
|
|
1331
|
-
a.toast("Workspace
|
|
1756
|
+
a.toast("Workspace added \u2014 it appears in the left sidebar; use New room on it");
|
|
1332
1757
|
} catch (e) {
|
|
1333
1758
|
setErr(e.message);
|
|
1334
1759
|
setBusy(false);
|
|
@@ -1339,18 +1764,14 @@ function WorkspaceDialog({ store }) {
|
|
|
1339
1764
|
{
|
|
1340
1765
|
open: true,
|
|
1341
1766
|
onClose: a.closeDialog,
|
|
1342
|
-
title: "
|
|
1767
|
+
title: "Add workspace",
|
|
1343
1768
|
closeLabel: "Close",
|
|
1344
|
-
description: "
|
|
1769
|
+
description: "Fallback: registers a dsh workspace (same as the native \u201CAdd workspace\u201D). It shows up in the left sidebar.",
|
|
1345
1770
|
footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1346
1771
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
|
|
1347
1772
|
/* @__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
1773
|
] }),
|
|
1349
1774
|
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
1775
|
/* @__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
1776
|
/* @__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
1777
|
err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: err }) : null
|
|
@@ -1363,7 +1784,7 @@ function BrowseDialog({ store }) {
|
|
|
1363
1784
|
const a = store.actions;
|
|
1364
1785
|
const [joining, setJoining] = (0, import_react.useState)(null);
|
|
1365
1786
|
const inputRef = (0, import_react.useRef)(null);
|
|
1366
|
-
const activeWs = (
|
|
1787
|
+
const [activeWs, setActiveWs] = (0, import_react.useState)(null);
|
|
1367
1788
|
(0, import_react.useEffect)(() => {
|
|
1368
1789
|
a.setSearchOpen(true);
|
|
1369
1790
|
requestAnimationFrame(() => inputRef.current?.focus());
|
|
@@ -1373,7 +1794,7 @@ function BrowseDialog({ store }) {
|
|
|
1373
1794
|
const join = async (r) => {
|
|
1374
1795
|
setJoining(r.roomId);
|
|
1375
1796
|
try {
|
|
1376
|
-
await a.join(r.invite, { name: r.name, description: r.description, tags: r.tags, public: true,
|
|
1797
|
+
await a.join(r.invite, { name: r.name, description: r.description, tags: r.tags, public: true, workspacePath: activeWs?.path, workspaceName: activeWs?.title });
|
|
1377
1798
|
} catch (e) {
|
|
1378
1799
|
a.toast(e.message);
|
|
1379
1800
|
} finally {
|
|
@@ -1388,9 +1809,10 @@ function BrowseDialog({ store }) {
|
|
|
1388
1809
|
title: "Browse public rooms",
|
|
1389
1810
|
closeLabel: "Close",
|
|
1390
1811
|
className: "rm-modal-wide",
|
|
1391
|
-
description: "Directory on VPS 62.234.113.56:80. Search by name or #tag
|
|
1812
|
+
description: "Directory on VPS 62.234.113.56:80. Search by name or #tag; joined rooms attach to the workspace below.",
|
|
1392
1813
|
footer: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Close" }),
|
|
1393
1814
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-browse", children: [
|
|
1815
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceSelect, { store, value: activeWs, onChange: setActiveWs }),
|
|
1394
1816
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search rm-browse-search", children: [
|
|
1395
1817
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
|
|
1396
1818
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -1434,13 +1856,15 @@ function Overlay({ store }) {
|
|
|
1434
1856
|
const s = useStore(store);
|
|
1435
1857
|
const d = s.dialog;
|
|
1436
1858
|
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,
|
|
1859
|
+
d?.kind === "create" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CreateDialog, { store, d }) : null,
|
|
1438
1860
|
d?.kind === "join" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JoinDialog, { store }) : null,
|
|
1439
1861
|
d?.kind === "invite" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InviteDialog, { store, d }) : null,
|
|
1440
1862
|
d?.kind === "leave" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LeaveDialog, { store, d }) : null,
|
|
1441
1863
|
d?.kind === "config" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConfigDialog, { store, d }) : null,
|
|
1442
1864
|
d?.kind === "workspace" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceDialog, { store }) : null,
|
|
1443
1865
|
d?.kind === "browse" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BrowseDialog, { store }) : null,
|
|
1866
|
+
d?.kind === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentDialog, { store, d }) : null,
|
|
1867
|
+
d?.kind === "merge" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeDialog, { store, d }) : null,
|
|
1444
1868
|
s.toast ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Toast, { text: s.toast, onDone: store.actions.clearToast, holdMs: 2200 }) : null
|
|
1445
1869
|
] });
|
|
1446
1870
|
}
|
|
@@ -1453,8 +1877,118 @@ function installStyles() {
|
|
|
1453
1877
|
document.head.appendChild(style);
|
|
1454
1878
|
return () => style.remove();
|
|
1455
1879
|
}
|
|
1880
|
+
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>';
|
|
1881
|
+
function installWorkspaceDecorator(store, native, onNewRoom) {
|
|
1882
|
+
if (typeof document === "undefined" || !native) return () => {
|
|
1883
|
+
};
|
|
1884
|
+
const wsFor = (key) => (native.list.getSnapshot().items || []).find((w) => w.workspaceId === key);
|
|
1885
|
+
const rowKey = (row) => (row.getAttribute("data-row-key") || "").slice("workspace:".length);
|
|
1886
|
+
const open = (key) => {
|
|
1887
|
+
const w = wsFor(key);
|
|
1888
|
+
if (w) onNewRoom({ id: w.workspaceId, path: w.path, title: wsTitle(w) });
|
|
1889
|
+
};
|
|
1890
|
+
const decorate = () => {
|
|
1891
|
+
for (const row of document.querySelectorAll('[data-row-key^="workspace:"]')) {
|
|
1892
|
+
const key = rowKey(row);
|
|
1893
|
+
if (!key || row.querySelector("[data-rm-ws-new-room]")) continue;
|
|
1894
|
+
const newSess = row.querySelector('button[aria-label^="New session in"]');
|
|
1895
|
+
if (!newSess) continue;
|
|
1896
|
+
let anchor = newSess;
|
|
1897
|
+
while (anchor.parentElement && anchor.parentElement !== row && !anchor.parentElement.querySelector('button[aria-label^="Workspace actions"]')) anchor = anchor.parentElement;
|
|
1898
|
+
if (!anchor.parentElement) continue;
|
|
1899
|
+
const label = (newSess.getAttribute("aria-label") || "").replace(/^New session in /, "");
|
|
1900
|
+
const btn = document.createElement("button");
|
|
1901
|
+
btn.type = "button";
|
|
1902
|
+
btn.className = newSess.className;
|
|
1903
|
+
btn.setAttribute("data-rm-ws-new-room", key);
|
|
1904
|
+
btn.setAttribute("aria-label", `New room in ${label}`);
|
|
1905
|
+
btn.title = "New room";
|
|
1906
|
+
btn.innerHTML = ROOM_SVG;
|
|
1907
|
+
btn.addEventListener("click", (e) => {
|
|
1908
|
+
e.preventDefault();
|
|
1909
|
+
e.stopPropagation();
|
|
1910
|
+
open(key);
|
|
1911
|
+
});
|
|
1912
|
+
anchor.parentElement.insertBefore(btn, anchor);
|
|
1913
|
+
}
|
|
1914
|
+
};
|
|
1915
|
+
let pendingKey = null;
|
|
1916
|
+
const onClickCapture = (e) => {
|
|
1917
|
+
const trigger = e.target instanceof Element ? e.target.closest('button[aria-label^="Workspace actions for"]') : null;
|
|
1918
|
+
if (!trigger) return;
|
|
1919
|
+
const row = trigger.closest('[data-row-key^="workspace:"]');
|
|
1920
|
+
pendingKey = row ? rowKey(row) : null;
|
|
1921
|
+
if (!pendingKey) return;
|
|
1922
|
+
const key = pendingKey;
|
|
1923
|
+
let tries = 0;
|
|
1924
|
+
const augment = () => {
|
|
1925
|
+
const menus = [...document.querySelectorAll('[role="menu"]')].filter((m) => m.querySelector('[role="menuitem"]'));
|
|
1926
|
+
const menu = menus[menus.length - 1];
|
|
1927
|
+
if (!menu) {
|
|
1928
|
+
if (++tries < 10) requestAnimationFrame(augment);
|
|
1929
|
+
return;
|
|
1930
|
+
}
|
|
1931
|
+
if (menu.querySelector("[data-rm-ws-menu]")) return;
|
|
1932
|
+
const tpl = menu.querySelector('[role="menuitem"]');
|
|
1933
|
+
const mk = (text, svg, onPick) => {
|
|
1934
|
+
const item = document.createElement("button");
|
|
1935
|
+
item.type = "button";
|
|
1936
|
+
item.setAttribute("role", "menuitem");
|
|
1937
|
+
item.setAttribute("data-rm-ws-menu", text);
|
|
1938
|
+
item.className = tpl.className;
|
|
1939
|
+
item.innerHTML = `<span style="display:inline-flex;width:16px;justify-content:center">${svg}</span><span>${text}</span>`;
|
|
1940
|
+
item.style.display = "flex";
|
|
1941
|
+
item.style.alignItems = "center";
|
|
1942
|
+
item.style.gap = "8px";
|
|
1943
|
+
item.addEventListener("click", (ev) => {
|
|
1944
|
+
ev.preventDefault();
|
|
1945
|
+
ev.stopPropagation();
|
|
1946
|
+
trigger.click();
|
|
1947
|
+
onPick();
|
|
1948
|
+
});
|
|
1949
|
+
return item;
|
|
1950
|
+
};
|
|
1951
|
+
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>';
|
|
1952
|
+
const first = menu.firstElementChild;
|
|
1953
|
+
menu.insertBefore(mk("New session", newSessSvg, () => {
|
|
1954
|
+
const row2 = document.querySelector(`[data-row-key="workspace:${CSS.escape(key)}"]`);
|
|
1955
|
+
row2?.querySelector('button[aria-label^="New session in"]')?.click();
|
|
1956
|
+
}), first);
|
|
1957
|
+
menu.insertBefore(mk("New room", ROOM_SVG, () => open(key)), first);
|
|
1958
|
+
};
|
|
1959
|
+
requestAnimationFrame(augment);
|
|
1960
|
+
};
|
|
1961
|
+
let raf = 0;
|
|
1962
|
+
const schedule = () => {
|
|
1963
|
+
if (!raf) raf = requestAnimationFrame(() => {
|
|
1964
|
+
raf = 0;
|
|
1965
|
+
decorate();
|
|
1966
|
+
});
|
|
1967
|
+
};
|
|
1968
|
+
const mo = new MutationObserver(schedule);
|
|
1969
|
+
mo.observe(document.body, { childList: true, subtree: true });
|
|
1970
|
+
document.addEventListener("click", onClickCapture, true);
|
|
1971
|
+
schedule();
|
|
1972
|
+
return () => {
|
|
1973
|
+
mo.disconnect();
|
|
1974
|
+
document.removeEventListener("click", onClickCapture, true);
|
|
1975
|
+
if (raf) cancelAnimationFrame(raf);
|
|
1976
|
+
for (const b of document.querySelectorAll("[data-rm-ws-new-room]")) b.remove();
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1456
1979
|
function apply(ctx) {
|
|
1457
1980
|
const store = createRoomsStore();
|
|
1981
|
+
let native = null;
|
|
1982
|
+
try {
|
|
1983
|
+
native = ctx.get ? ctx.get("workspaces") : ctx.workspaces;
|
|
1984
|
+
} catch {
|
|
1985
|
+
native = ctx.workspaces || null;
|
|
1986
|
+
}
|
|
1987
|
+
store.native = native || null;
|
|
1988
|
+
store.nativeList = native && native.list ? {
|
|
1989
|
+
subscribe: (listener) => native.list.subscribe(listener),
|
|
1990
|
+
getSnapshot: () => native.list.getSnapshot()
|
|
1991
|
+
} : null;
|
|
1458
1992
|
ctx.effect(() => installStyles(), "dsh-room: styles");
|
|
1459
1993
|
ctx.effect(() => () => store.dispose(), "dsh-room: store");
|
|
1460
1994
|
const info = () => ctx.layout.panelInfo;
|
|
@@ -1464,27 +1998,12 @@ function apply(ctx) {
|
|
|
1464
1998
|
} catch {
|
|
1465
1999
|
}
|
|
1466
2000
|
};
|
|
2001
|
+
store.onRoomOpened = ensurePanel;
|
|
1467
2002
|
ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({ name: "sidebar.panellist", id: PANEL_ID, order: 11, label: () => "DSH Room" }, PanelIcon));
|
|
1468
2003
|
ctx.slots.inject("main", () => ctx.slots.register({ name: "main", key: PANEL_ID, inject: () => ({ store }) }, RoomsMain));
|
|
1469
2004
|
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
|
-
});
|
|
2005
|
+
ctx.slots.inject("sidebar.session.row.leading", () => ctx.slots.register({ name: "sidebar.session.row.leading", id: "dsh-room-session-mark", inject: () => ({ store }) }, RoomSessionMark));
|
|
2006
|
+
ctx.effect(() => installWorkspaceDecorator(store, native, (workspace) => store.actions.openDialog({ kind: "create", workspace })), "dsh-room: workspace New room");
|
|
1488
2007
|
}
|
|
1489
2008
|
|
|
1490
2009
|
return module.exports;
|
|
@@ -1528,7 +2047,7 @@ var import_react = require("react");
|
|
|
1528
2047
|
var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
1529
2048
|
|
|
1530
2049
|
// 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';
|
|
2050
|
+
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\n/* \u2500\u2500 local multi-agent (0.3) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-dot-busy { background: var(--dsw-alias-state-warning-primary, #f0a020); }\n.rm-agent-tag { display: inline-flex; align-items: center; gap: 3px; margin-left: 6px; padding: 0 6px; border-radius: 999px; font-size: 10.5px; line-height: 17px; color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 12%, transparent); white-space: nowrap; }\n.rm-msg-id { margin-left: 6px; font-size: 11px; color: var(--rm-quiet); font-family: var(--dsw-font-mono, ui-monospace, monospace); }\n.rm-agent-row { display: flex; align-items: center; gap: 8px; padding: 6px 4px; border-radius: 8px; }\n.rm-agent-row:hover { background: var(--rm-hover); }\n.rm-agent-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }\n.rm-agent-name { font-size: 13px; font-weight: 600; color: var(--rm-label); display: flex; align-items: center; }\n.rm-agent-sub { font-size: 11px; color: var(--rm-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.rm-agent-row .rm-member-actions { display: inline-flex !important; opacity: 0.65; }\n.rm-agent-row:hover .rm-member-actions { opacity: 1; }\n.rm-num-row { display: flex; gap: 8px; margin: 4px 0 8px; }\n.rm-num-setting { flex: 1; display: flex; flex-direction: column; gap: 3px; font-size: 11px; color: var(--rm-muted); }\n.rm-num-setting input { width: 100%; box-sizing: border-box; padding: 5px 8px; border: 1px solid var(--rm-border); border-radius: 7px; background: var(--rm-bg); color: var(--rm-label); font: inherit; font-size: 13px; }\n.rm-runs { display: flex; flex-direction: column; gap: 4px; }\n.rm-run { display: grid; grid-template-columns: 22px auto 1fr auto; align-items: center; gap: 6px; padding: 5px 6px; border-radius: 7px; font-size: 12px; border: 1px solid var(--rm-border); }\n.rm-run-running { border-color: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #f0a020) 55%, transparent); background: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #f0a020) 8%, transparent); }\n.rm-run-queued, .rm-run-approval { border-style: dashed; }\n.rm-run-done, .rm-run-stopped, .rm-run-failed { opacity: 0.7; }\n.rm-run-pos { font-size: 11px; color: var(--rm-quiet); text-align: center; font-variant-numeric: tabular-nums; }\n.rm-run-who { font-weight: 600; color: var(--rm-label); white-space: nowrap; }\n.rm-run-what { color: var(--rm-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }\n.rm-stop-btn { display: inline-flex; align-items: center; gap: 5px; height: 26px; padding: 0 10px; border-radius: 999px; border: 1px solid color-mix(in srgb, var(--dsw-alias-state-danger-primary, #e5484d) 60%, transparent); background: color-mix(in srgb, var(--dsw-alias-state-danger-primary, #e5484d) 10%, transparent); color: var(--dsw-alias-state-danger-primary, #e5484d); font-size: 12px; cursor: pointer; white-space: nowrap; }\n.rm-stop-btn:hover { background: color-mix(in srgb, var(--dsw-alias-state-danger-primary, #e5484d) 18%, transparent); }\n.rm-badge-local { color: var(--dsw-alias-state-success-primary, #2bb673); }\n.rm-session-agent { color: var(--rm-accent, #5b8cff); }\n.rm-preset-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }\n.rm-preset { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-width: 150px; flex: 1; padding: 8px 10px; border: 1px solid var(--rm-border); border-radius: 10px; background: transparent; color: var(--rm-label); text-align: left; cursor: pointer; font: inherit; font-size: 12px; }\n.rm-preset span { color: var(--rm-muted); font-size: 11px; line-height: 1.35; }\n.rm-preset.is-on { border-color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 12%, transparent); }\n.rm-preset-check { flex-direction: row; align-items: center; gap: 8px; min-width: 120px; flex: 0 1 auto; }\n.rm-local-box { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border: 1px dashed var(--rm-border); border-radius: 12px; }\n.rm-textarea { width: 100%; box-sizing: border-box; min-height: 84px; resize: vertical; font: inherit; font-size: 13px; padding: 8px 10px; }\n.rm-switch-col { display: flex; flex-direction: column; gap: 6px; }\n.rm-modal-xl { width: min(1180px, 96vw) !important; max-width: 96vw !important; }\n.rm-merge { display: grid; grid-template-columns: 260px 1fr; gap: 14px; min-height: 420px; }\n.rm-merge-list { display: flex; flex-direction: column; gap: 6px; border-right: 1px solid var(--rm-border); padding-right: 12px; }\n.rm-merge-agent { display: flex; align-items: center; gap: 8px; padding: 8px; border: 1px solid transparent; border-radius: 10px; background: transparent; color: var(--rm-label); cursor: pointer; text-align: left; font: inherit; }\n.rm-merge-agent:hover { background: var(--rm-hover); }\n.rm-merge-agent.is-on { border-color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 10%, transparent); }\n.rm-merge-agent-body { flex: 1; min-width: 0; display: flex; flex-direction: column; font-size: 12px; }\n.rm-merge-agent-body .rm-mono { font-size: 10.5px; color: var(--rm-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.rm-merge-main { min-width: 0; display: flex; flex-direction: column; gap: 8px; }\n.rm-merge-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; font-size: 13px; }\n.rm-merge-files { display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: 12px; }\n.rm-confirm-text { font-size: 12px; color: var(--rm-label); margin-right: 4px; }\n.rm-diff { margin: 0; max-height: 52vh; overflow: auto; padding: 8px 0; border: 1px solid var(--rm-border); border-radius: 10px; background: var(--rm-panel); font-family: var(--dsw-font-mono, ui-monospace, monospace); font-size: 12px; line-height: 1.5; }\n.rm-diff > div { padding: 0 12px; white-space: pre; }\n.rm-diff-add { background: color-mix(in srgb, #2bb673 16%, transparent); }\n.rm-diff-del { background: color-mix(in srgb, #e5484d 16%, transparent); }\n.rm-diff-hunk { color: var(--rm-accent, #5b8cff); }\n.rm-diff-head, .rm-diff-file { color: var(--rm-muted); font-weight: 600; }\n.rm-diff-addn { color: #2bb673; }\n.rm-diff-deln { color: #e5484d; }\n.rm-ok { padding: 8px 10px; border-radius: 8px; font-size: 12.5px; color: var(--dsw-alias-state-success-primary, #2bb673); background: color-mix(in srgb, #2bb673 10%, transparent); }\n';
|
|
1532
2051
|
|
|
1533
2052
|
// src/client/store.js
|
|
1534
2053
|
var API = "/api/collab";
|
|
@@ -1644,13 +2163,21 @@ function createRoomsStore() {
|
|
|
1644
2163
|
searchTimer = setTimeout(() => void runSearch(query), 300);
|
|
1645
2164
|
},
|
|
1646
2165
|
async create(form) {
|
|
1647
|
-
const r = await post("/rooms", { ...form, workspaceId: form.workspaceId || snap.activeWorkspaceId });
|
|
2166
|
+
const r = await post("/rooms", { ...form, workspaceId: form.workspacePath ? void 0 : form.workspaceId || snap.activeWorkspaceId });
|
|
2167
|
+
try {
|
|
2168
|
+
api.onRoomOpened && api.onRoomOpened();
|
|
2169
|
+
} catch {
|
|
2170
|
+
}
|
|
1648
2171
|
set({ selected: r.roomId, dialog: { kind: "invite", roomId: r.roomId, name: r.name, invite: r.invite, public: r.public, listed: r.listed } });
|
|
1649
2172
|
void loadMessages(r.roomId);
|
|
1650
2173
|
return r;
|
|
1651
2174
|
},
|
|
1652
2175
|
async join(invite, info) {
|
|
1653
|
-
const r = await post("/join", { invite, workspaceId: snap.activeWorkspaceId, ...info || {} });
|
|
2176
|
+
const r = await post("/join", { invite, ...info && info.workspacePath ? {} : { workspaceId: snap.activeWorkspaceId }, ...info || {} });
|
|
2177
|
+
try {
|
|
2178
|
+
api.onRoomOpened && api.onRoomOpened();
|
|
2179
|
+
} catch {
|
|
2180
|
+
}
|
|
1654
2181
|
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
2182
|
void loadMessages(r.roomId);
|
|
1656
2183
|
toast(`Joined ${r.name}`);
|
|
@@ -1698,9 +2225,40 @@ function createRoomsStore() {
|
|
|
1698
2225
|
},
|
|
1699
2226
|
async mcp(roomId, tool, args, target) {
|
|
1700
2227
|
return post(`/rooms/${encodeURIComponent(roomId)}/mcp`, { tool, args, target });
|
|
2228
|
+
},
|
|
2229
|
+
// ── local multi-agent ──
|
|
2230
|
+
async presets() {
|
|
2231
|
+
return (await call("/presets")).presets;
|
|
2232
|
+
},
|
|
2233
|
+
async addAgent(roomId, body) {
|
|
2234
|
+
return post(`/rooms/${encodeURIComponent(roomId)}/agents`, body);
|
|
2235
|
+
},
|
|
2236
|
+
async updateAgent(roomId, id, body) {
|
|
2237
|
+
return post(`/rooms/${encodeURIComponent(roomId)}/agents/${encodeURIComponent(id)}`, body);
|
|
2238
|
+
},
|
|
2239
|
+
async removeAgent(roomId, id) {
|
|
2240
|
+
return post(`/rooms/${encodeURIComponent(roomId)}/agents/${encodeURIComponent(id)}?op=delete`);
|
|
2241
|
+
},
|
|
2242
|
+
async saveLocal(roomId, patch) {
|
|
2243
|
+
return post(`/rooms/${encodeURIComponent(roomId)}/local`, patch);
|
|
2244
|
+
},
|
|
2245
|
+
async setTransport(roomId, target, force) {
|
|
2246
|
+
return post(`/rooms/${encodeURIComponent(roomId)}/transport`, { target, force: !!force });
|
|
2247
|
+
},
|
|
2248
|
+
async stop(roomId, agentId) {
|
|
2249
|
+
return post(`/rooms/${encodeURIComponent(roomId)}/stop`, agentId ? { agentId } : {});
|
|
2250
|
+
},
|
|
2251
|
+
async diffs(roomId) {
|
|
2252
|
+
return call(`/rooms/${encodeURIComponent(roomId)}/diffs`);
|
|
2253
|
+
},
|
|
2254
|
+
async merge(roomId, agentId) {
|
|
2255
|
+
return post(`/rooms/${encodeURIComponent(roomId)}/merge`, { agentId, approve: true });
|
|
2256
|
+
},
|
|
2257
|
+
async discard(roomId, agentId) {
|
|
2258
|
+
return post(`/rooms/${encodeURIComponent(roomId)}/discard`, { agentId, approve: true });
|
|
1701
2259
|
}
|
|
1702
2260
|
};
|
|
1703
|
-
|
|
2261
|
+
const api = {
|
|
1704
2262
|
getSnapshot: () => snap,
|
|
1705
2263
|
subscribe(fn) {
|
|
1706
2264
|
subs.add(fn);
|
|
@@ -1713,14 +2271,20 @@ function createRoomsStore() {
|
|
|
1713
2271
|
es = null;
|
|
1714
2272
|
subs.clear();
|
|
1715
2273
|
clearTimeout(searchTimer);
|
|
1716
|
-
}
|
|
2274
|
+
},
|
|
2275
|
+
/** Native dsh workspaces service (set by apply). */
|
|
2276
|
+
native: null,
|
|
2277
|
+
nativeList: null,
|
|
2278
|
+
/** Called after a room is created/joined (apply switches to the DSH Room panel). */
|
|
2279
|
+
onRoomOpened: null
|
|
1717
2280
|
};
|
|
2281
|
+
return api;
|
|
1718
2282
|
}
|
|
1719
2283
|
|
|
1720
2284
|
// src/client/index.jsx
|
|
1721
2285
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1722
2286
|
var name = "dsh-room-client";
|
|
1723
|
-
var inject = ["slots", "layout"];
|
|
2287
|
+
var inject = ["slots", "layout", "workspaces"];
|
|
1724
2288
|
var PANEL_ID = "collab";
|
|
1725
2289
|
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
2290
|
var RoomsIcon = ({ size }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size, children: [
|
|
@@ -1733,7 +2297,6 @@ var SearchIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { child
|
|
|
1733
2297
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m10.2 10.2 3.3 3.3" })
|
|
1734
2298
|
] });
|
|
1735
2299
|
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
2300
|
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
2301
|
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
2302
|
var TaskIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { children: [
|
|
@@ -1748,6 +2311,17 @@ var LockIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size: 1
|
|
|
1748
2311
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3.5", y: "7", width: "9", height: "6.25", rx: "1.5" }),
|
|
1749
2312
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5.5 7V5.25a2.5 2.5 0 0 1 5 0V7" })
|
|
1750
2313
|
] });
|
|
2314
|
+
var BotIcon = ({ size = 14 }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size, children: [
|
|
2315
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3", y: "5", width: "10", height: "7.5", rx: "2" }),
|
|
2316
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 2.5V5M6 8.5h.01M10 8.5h.01M6.25 10.75h3.5" })
|
|
2317
|
+
] });
|
|
2318
|
+
var StopIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Svg, { size: 12, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5" }) });
|
|
2319
|
+
var MergeIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size: 14, children: [
|
|
2320
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "5", cy: "3.75", r: "1.5" }),
|
|
2321
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "5", cy: "12.25", r: "1.5" }),
|
|
2322
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "11", cy: "8", r: "1.5" }),
|
|
2323
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 5.25v5.5M5 5.5c0 2 1.5 2.5 4.5 2.5" })
|
|
2324
|
+
] });
|
|
1751
2325
|
var PeopleIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size: 14, children: [
|
|
1752
2326
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "5.5", r: "2.25" }),
|
|
1753
2327
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2.5 13c.4-2 1.8-3.25 3.5-3.25S9.1 11 9.5 13" }),
|
|
@@ -1778,220 +2352,133 @@ var STATUS_TONE = { open: "info", claimed: "warning", done: "success", failed: "
|
|
|
1778
2352
|
function useStore(store) {
|
|
1779
2353
|
return (0, import_react.useSyncExternalStore)(store.subscribe, store.getSnapshot);
|
|
1780
2354
|
}
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
}
|
|
1784
|
-
|
|
1785
|
-
|
|
2355
|
+
var EMPTY_WS = { items: [] };
|
|
2356
|
+
var NO_SUB = () => () => {
|
|
2357
|
+
};
|
|
2358
|
+
var NO_SNAP = () => EMPTY_WS;
|
|
2359
|
+
function useNativeWorkspaces(store) {
|
|
2360
|
+
const src = store.nativeList;
|
|
2361
|
+
const snap = (0, import_react.useSyncExternalStore)(src ? src.subscribe : NO_SUB, src ? src.getSnapshot : NO_SNAP);
|
|
2362
|
+
return snap && snap.items || [];
|
|
2363
|
+
}
|
|
2364
|
+
var samePath = (a, b) => !!a && !!b && a.replace(/[\\/]+$/, "") === b.replace(/[\\/]+$/, "");
|
|
2365
|
+
function groupRooms(rooms, workspaces) {
|
|
2366
|
+
const groups = workspaces.map((w) => ({ ws: w, rooms: [] }));
|
|
2367
|
+
const other = [];
|
|
2368
|
+
for (const r of rooms) {
|
|
2369
|
+
const path = r.memberConfig && r.memberConfig.workspacePath;
|
|
2370
|
+
const g = groups.find((x) => samePath(x.ws.path, path));
|
|
2371
|
+
if (g) g.rooms.push(r);
|
|
2372
|
+
else other.push(r);
|
|
2373
|
+
}
|
|
2374
|
+
return { groups, other };
|
|
2375
|
+
}
|
|
2376
|
+
var wsTitle = (w) => w.title || (w.path || "").split(/[\\/]/).filter(Boolean).pop() || w.path;
|
|
2377
|
+
function RoomItem({ r, selected, onClick }) {
|
|
2378
|
+
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: [
|
|
2379
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-avatar-wrap", children: [
|
|
2380
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: r.name, size: 26 }),
|
|
2381
|
+
/* @__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)" } })
|
|
2382
|
+
] }),
|
|
2383
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-room-item-body", children: [
|
|
2384
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-room-item-name", children: r.name }),
|
|
2385
|
+
/* @__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") })
|
|
2386
|
+
] }),
|
|
2387
|
+
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, {}) })
|
|
2388
|
+
] });
|
|
1786
2389
|
}
|
|
1787
|
-
function
|
|
2390
|
+
function RoomsNav({ store }) {
|
|
1788
2391
|
const s = useStore(store);
|
|
1789
2392
|
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
|
|
1881
|
-
] }),
|
|
1882
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-bar", children: [
|
|
1883
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-ws-chip", onClick: () => a.openDialog({ kind: "workspace" }), title: activeWs.path, children: [
|
|
1884
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
|
|
1885
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: activeWs.name })
|
|
1886
|
-
] }),
|
|
1887
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-path", title: activeWs.path, children: activeWs.path }),
|
|
1888
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-new-room", "data-rm-new-room": "", onClick: () => {
|
|
1889
|
-
a.openDialog({ kind: "create" });
|
|
1890
|
-
ensurePanel();
|
|
1891
|
-
}, children: [
|
|
1892
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}),
|
|
1893
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "New Room" })
|
|
1894
|
-
] })
|
|
2393
|
+
const workspaces = useNativeWorkspaces(store);
|
|
2394
|
+
const [q, setQ] = (0, import_react.useState)("");
|
|
2395
|
+
const qq = q.trim().toLowerCase();
|
|
2396
|
+
const match = (r) => !qq || r.name.toLowerCase().includes(qq) || (r.description || "").toLowerCase().includes(qq) || (r.tags || []).some((t) => ("#" + t).includes(qq));
|
|
2397
|
+
const { groups, other } = groupRooms((s.rooms || []).filter(match), workspaces);
|
|
2398
|
+
const sel = (r) => a.select(r.roomId);
|
|
2399
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("nav", { className: "rm-nav", "aria-label": "Rooms by workspace", "data-rm-nav": "", children: [
|
|
2400
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-nav-head", children: [
|
|
2401
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-side-title", children: "Rooms" }),
|
|
2402
|
+
/* @__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, {}) }),
|
|
2403
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-icon-btn", title: "Join by invite", "aria-label": "Join by invite", onClick: () => a.openDialog({ kind: "join" }), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkIcon, {}) })
|
|
1895
2404
|
] }),
|
|
1896
|
-
|
|
2405
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search", children: [
|
|
1897
2406
|
/* @__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)(
|
|
2407
|
+
/* @__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" })
|
|
2408
|
+
] }),
|
|
2409
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-nav-scroll", children: [
|
|
2410
|
+
groups.map(({ ws, rooms }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-group", "data-rm-ws-group": ws.workspaceId, children: [
|
|
2411
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-head", title: ws.path, children: [
|
|
2412
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}),
|
|
2413
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-name", children: wsTitle(ws) }),
|
|
2414
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1908
2415
|
"button",
|
|
1909
2416
|
{
|
|
1910
2417
|
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: [
|
|
2418
|
+
className: "rm-icon-btn rm-ws-add",
|
|
2419
|
+
title: `New room in ${wsTitle(ws)}`,
|
|
2420
|
+
"aria-label": `New room in ${wsTitle(ws)}`,
|
|
2421
|
+
onClick: () => a.openDialog({ kind: "create", workspace: { id: ws.workspaceId, path: ws.path, title: wsTitle(ws) } }),
|
|
2422
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {})
|
|
2423
|
+
}
|
|
2424
|
+
)
|
|
2425
|
+
] }),
|
|
2426
|
+
rooms.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomItem, { r, selected: r.roomId === s.selected, onClick: () => sel(r) }, r.roomId)),
|
|
2427
|
+
!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
|
|
2428
|
+
] }, ws.workspaceId)),
|
|
2429
|
+
other.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-group", children: [
|
|
2430
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-ws-head", children: [
|
|
1948
2431
|
/* @__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" : ""
|
|
2432
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-ws-name", children: "Other rooms" })
|
|
1959
2433
|
] }),
|
|
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
|
-
] })
|
|
2434
|
+
other.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomItem, { r, selected: r.roomId === s.selected, onClick: () => sel(r) }, r.roomId))
|
|
2435
|
+
] }) : null,
|
|
2436
|
+
!workspaces.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-empty-hint", children: [
|
|
2437
|
+
"No workspaces yet. Add one with the native \u201CAdd workspace\u201D in the left sidebar, then use \u201CNew room\u201D on it.",
|
|
2438
|
+
/* @__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
2439
|
] }) : null
|
|
1984
2440
|
] })
|
|
1985
2441
|
] });
|
|
1986
2442
|
}
|
|
1987
|
-
function
|
|
2443
|
+
function RoomSessionMark({ store, sessionId }) {
|
|
2444
|
+
const s = useStore(store);
|
|
2445
|
+
for (const r of s.rooms || []) {
|
|
2446
|
+
const ag = (r.local && r.local.agents || []).find((x) => x.sessionId === sessionId);
|
|
2447
|
+
if (ag) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-session-mark rm-session-agent", title: `DSH Room \xB7 ${r.name} \xB7 ${ag.name} (${ag.agentId})`, "aria-label": `DSH Room ${r.name} agent ${ag.name}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 13 }) });
|
|
2448
|
+
}
|
|
2449
|
+
const room = (s.rooms || []).find((r) => r.sessionId === sessionId);
|
|
2450
|
+
if (!room) return null;
|
|
2451
|
+
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 }) });
|
|
2452
|
+
}
|
|
2453
|
+
function WorkspaceSelect({ store, value, onChange, id }) {
|
|
2454
|
+
const workspaces = useNativeWorkspaces(store);
|
|
2455
|
+
(0, import_react.useEffect)(() => {
|
|
2456
|
+
if (!value && workspaces[0]) onChange({ id: workspaces[0].workspaceId, path: workspaces[0].path, title: wsTitle(workspaces[0]) });
|
|
2457
|
+
}, [workspaces.length]);
|
|
2458
|
+
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)." });
|
|
2459
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { id, className: "rm-input", value: value ? value.path : "", onChange: (e) => {
|
|
2460
|
+
const w = workspaces.find((x) => x.path === e.target.value);
|
|
2461
|
+
if (w) onChange({ id: w.workspaceId, path: w.path, title: wsTitle(w) });
|
|
2462
|
+
}, children: workspaces.map((w) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", { value: w.path, children: [
|
|
2463
|
+
wsTitle(w),
|
|
2464
|
+
" \u2014 ",
|
|
2465
|
+
w.path
|
|
2466
|
+
] }, w.workspaceId)) });
|
|
2467
|
+
}
|
|
2468
|
+
function renderMentions(text) {
|
|
2469
|
+
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);
|
|
2470
|
+
}
|
|
2471
|
+
function PanelIcon({ size }) {
|
|
2472
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsIcon, { size });
|
|
2473
|
+
}
|
|
2474
|
+
function Message({ m, showAuthor, who }) {
|
|
1988
2475
|
if (m.kind === "task.create") {
|
|
1989
2476
|
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: [
|
|
1990
2477
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-task-head", children: [
|
|
1991
2478
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}),
|
|
1992
2479
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
1993
2480
|
"Task \xB7 ",
|
|
1994
|
-
m.from
|
|
2481
|
+
who && who.name || m.from
|
|
1995
2482
|
] }),
|
|
1996
2483
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "info", children: "open" }),
|
|
1997
2484
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-time", children: fmtTime(m.ts) })
|
|
@@ -2004,7 +2491,7 @@ function Message({ m, showAuthor }) {
|
|
|
2004
2491
|
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-head", children: [
|
|
2005
2492
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}),
|
|
2006
2493
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
2007
|
-
m.self ? "You" : m.from,
|
|
2494
|
+
m.self ? "You" : who && who.name || m.from,
|
|
2008
2495
|
" ",
|
|
2009
2496
|
m.kind === "task.claim" ? "claimed" : "updated",
|
|
2010
2497
|
" task"
|
|
@@ -2024,14 +2511,20 @@ function Message({ m, showAuthor }) {
|
|
|
2024
2511
|
showAuthor ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: m.from, size: 28 }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {}),
|
|
2025
2512
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
2026
2513
|
showAuthor ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-msg-head", children: [
|
|
2027
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-author", children: m.from }),
|
|
2514
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-author", children: who && who.name ? who.name : m.from }),
|
|
2515
|
+
who && who.name ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-id", children: m.from }) : null,
|
|
2516
|
+
who && who.local ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-tag", children: [
|
|
2517
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 11 }),
|
|
2518
|
+
who.moderator ? "moderator" : "local agent"
|
|
2519
|
+
] }) : null,
|
|
2028
2520
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-time", children: fmtTime(m.ts) })
|
|
2029
2521
|
] }) : null,
|
|
2030
2522
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-msg-body", children: renderMentions(m.text) })
|
|
2031
2523
|
] })
|
|
2032
2524
|
] });
|
|
2033
2525
|
}
|
|
2034
|
-
function ChatBody({ messages }) {
|
|
2526
|
+
function ChatBody({ messages, members }) {
|
|
2527
|
+
const byId = (0, import_react.useMemo)(() => Object.fromEntries((members || []).map((x) => [x.agentId, x])), [members]);
|
|
2035
2528
|
const ref = (0, import_react.useRef)(null);
|
|
2036
2529
|
(0, import_react.useEffect)(() => {
|
|
2037
2530
|
const el = ref.current;
|
|
@@ -2047,7 +2540,7 @@ function ChatBody({ messages }) {
|
|
|
2047
2540
|
prev = null;
|
|
2048
2541
|
}
|
|
2049
2542
|
const showAuthor = !prev || prev.from !== m.from || m.ts - prev.ts > 5 * 6e4;
|
|
2050
|
-
nodes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Message, { m, showAuthor }, m.id));
|
|
2543
|
+
nodes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Message, { m, showAuthor, who: byId[m.from] }, m.id));
|
|
2051
2544
|
prev = m;
|
|
2052
2545
|
}
|
|
2053
2546
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-messages", ref, children: nodes.length ? nodes : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-chat-hint", children: "No messages yet. Send a message, @mention an agent to wake it, or create a task from the composer." }) });
|
|
@@ -2074,7 +2567,7 @@ function MentionMenu({ items, active, onPick, onHover }) {
|
|
|
2074
2567
|
onPick(it);
|
|
2075
2568
|
},
|
|
2076
2569
|
children: [
|
|
2077
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: it.id === "all" ? "all" : it.id, size: 22 }),
|
|
2570
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: it.id === "all" ? "all" : it.name || it.id, size: 22 }),
|
|
2078
2571
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-mention-item-body", children: [
|
|
2079
2572
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-mention-item-name", children: [
|
|
2080
2573
|
"@",
|
|
@@ -2082,6 +2575,10 @@ function MentionMenu({ items, active, onPick, onHover }) {
|
|
|
2082
2575
|
] }),
|
|
2083
2576
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mention-item-meta", children: it.hint })
|
|
2084
2577
|
] }),
|
|
2578
|
+
it.local ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-tag", children: [
|
|
2579
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 11 }),
|
|
2580
|
+
"local"
|
|
2581
|
+
] }) : null,
|
|
2085
2582
|
it.online === false ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "quiet", children: "offline" }) : null
|
|
2086
2583
|
]
|
|
2087
2584
|
},
|
|
@@ -2106,14 +2603,20 @@ function Composer({ room, store, draft, setDraft }) {
|
|
|
2106
2603
|
if (!mq) return [];
|
|
2107
2604
|
const q = mq.query.toLowerCase();
|
|
2108
2605
|
const members = (room.members || []).filter((m) => !m.self);
|
|
2109
|
-
const
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2606
|
+
const localAgents = room.local && room.local.agents || [];
|
|
2607
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2608
|
+
const known = [];
|
|
2609
|
+
for (const la of localAgents) {
|
|
2610
|
+
seen.add(la.agentId);
|
|
2611
|
+
known.push({ id: la.agentId, name: la.name, local: true, hint: `${la.name} \xB7 local agent${la.moderator ? " \xB7 moderator" : ""}${la.state && la.state !== "idle" ? ` \xB7 ${la.state}` : ""}`, online: la.online !== false });
|
|
2612
|
+
}
|
|
2613
|
+
for (const m of members) {
|
|
2614
|
+
if (seen.has(m.agentId)) continue;
|
|
2615
|
+
known.push({ id: m.agentId, name: m.name, hint: `${m.name ? m.name + " \xB7 " : ""}${m.hostedBy ? "remote agent" : "member"}${m.online === false ? " \xB7 offline" : " \xB7 wake"}`, online: m.online !== false });
|
|
2616
|
+
}
|
|
2617
|
+
const all = { id: "all", hint: localAgents.length ? "Everyone \xB7 wakes all local agents in parallel" : "Everyone \xB7 wake all members", online: true };
|
|
2115
2618
|
const list = [all, ...known];
|
|
2116
|
-
return list.filter((it) => !q || it.id.toLowerCase().startsWith(q) ||
|
|
2619
|
+
return list.filter((it) => !q || it.id.toLowerCase().startsWith(q) || it.name && it.name.toLowerCase().startsWith(q));
|
|
2117
2620
|
})();
|
|
2118
2621
|
const mc = room.memberConfig || {};
|
|
2119
2622
|
const modelLabel = mc.model || "Default model";
|
|
@@ -2151,6 +2654,23 @@ function Composer({ room, store, draft, setDraft }) {
|
|
|
2151
2654
|
a.toast(e.message);
|
|
2152
2655
|
}
|
|
2153
2656
|
};
|
|
2657
|
+
const syncCaret = (el) => {
|
|
2658
|
+
if (el) setCaret(el.selectionStart == null ? el.value.length : el.selectionStart);
|
|
2659
|
+
};
|
|
2660
|
+
const insertMention = (it) => {
|
|
2661
|
+
if (!mq || !it) return;
|
|
2662
|
+
const next = draft.slice(0, mq.start) + "@" + it.id + " " + draft.slice(mq.end);
|
|
2663
|
+
const pos = mq.start + it.id.length + 2;
|
|
2664
|
+
setDraft(next);
|
|
2665
|
+
setCaret(pos);
|
|
2666
|
+
requestAnimationFrame(() => {
|
|
2667
|
+
const el = taRef.current;
|
|
2668
|
+
if (el) {
|
|
2669
|
+
el.focus();
|
|
2670
|
+
el.setSelectionRange(pos, pos);
|
|
2671
|
+
}
|
|
2672
|
+
});
|
|
2673
|
+
};
|
|
2154
2674
|
const submit = async () => {
|
|
2155
2675
|
const text = draft.trim();
|
|
2156
2676
|
if (!text || busy) return;
|
|
@@ -2347,7 +2867,15 @@ function RoomDetail({ room, messages, store, mention, assign }) {
|
|
|
2347
2867
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: m.agentId, size: 26 }),
|
|
2348
2868
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-dot rm-dot-" + (m.online === false ? "offline" : "online"), style: { position: "absolute", right: -1, bottom: -1, border: "2px solid var(--rm-bg)" } })
|
|
2349
2869
|
] }),
|
|
2350
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-member-name", children: m.
|
|
2870
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-member-name", children: m.name ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
2871
|
+
m.name,
|
|
2872
|
+
" ",
|
|
2873
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-id", children: m.agentId })
|
|
2874
|
+
] }) : m.agentId }),
|
|
2875
|
+
m.local ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-tag", children: [
|
|
2876
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 11 }),
|
|
2877
|
+
m.moderator ? "moderator" : "local"
|
|
2878
|
+
] }) : m.hostedBy && !m.self ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-agent-tag", children: "remote agent" }) : null,
|
|
2351
2879
|
m.self ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "quiet", children: "you" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-member-actions", children: [
|
|
2352
2880
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `@mention ${m.agentId}`, onClick: () => mention(m.agentId), children: "@" }),
|
|
2353
2881
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `Assign a task to ${m.agentId}`, onClick: () => assign(m.agentId), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}) })
|
|
@@ -2355,6 +2883,9 @@ function RoomDetail({ room, messages, store, mention, assign }) {
|
|
|
2355
2883
|
] }, m.agentId)),
|
|
2356
2884
|
!room.members.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: room.status === "online" ? "Only you are here." : "Offline." }) : null
|
|
2357
2885
|
] }),
|
|
2886
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LocalAgentsSection, { room, store, mention }),
|
|
2887
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(OrchestrationSection, { room, store }),
|
|
2888
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(IsolationSection, { room, store }),
|
|
2358
2889
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", children: [
|
|
2359
2890
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
|
|
2360
2891
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}),
|
|
@@ -2377,8 +2908,10 @@ function RoomDetail({ room, messages, store, mention, assign }) {
|
|
|
2377
2908
|
"Invite"
|
|
2378
2909
|
] }),
|
|
2379
2910
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: room.public ? "Public: listed in the relay directory with its key. Anyone can find, join, and read this room." : "Private: only agents holding the invite can join. The invite contains the room key \u2014 share it only with agents you trust." }),
|
|
2911
|
+
room.transport === "local" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Local-only room: switch Transport to Relay or LAN before inviting remote hosts." }) : null,
|
|
2380
2912
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: copyInvite, icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkIcon, {}), children: "Copy invite" }) })
|
|
2381
2913
|
] }),
|
|
2914
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TransportSection, { room, store }),
|
|
2382
2915
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", children: [
|
|
2383
2916
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-detail-h", children: "About" }),
|
|
2384
2917
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dl", { className: "rm-about", children: [
|
|
@@ -2391,7 +2924,7 @@ function RoomDetail({ room, messages, store, mention, assign }) {
|
|
|
2391
2924
|
" Private"
|
|
2392
2925
|
] }) }),
|
|
2393
2926
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: "Transport" }),
|
|
2394
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: room.transport === "lan" ? "LAN / VPS (Node relay)" : "Relay (Cloudflare)" }),
|
|
2927
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: room.transport === "local" ? "Local only (in-process loopback)" : room.transport === "lan" ? "LAN / VPS (Node relay)" : "Relay (Cloudflare)" }),
|
|
2395
2928
|
room.memberConfig?.workspaceLabel ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
2396
2929
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: "Workspace" }),
|
|
2397
2930
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: room.memberConfig.workspaceLabel })
|
|
@@ -2418,33 +2951,392 @@ function RoomDetail({ room, messages, store, mention, assign }) {
|
|
|
2418
2951
|
] })
|
|
2419
2952
|
] });
|
|
2420
2953
|
}
|
|
2421
|
-
|
|
2954
|
+
var RUN_TONE = { running: "warning", queued: "info", approval: "info", done: "success", failed: "danger", stopped: "quiet" };
|
|
2955
|
+
var MODEL_CHOICES = ["", "deepseek-chat", "deepseek-reasoner"];
|
|
2956
|
+
function LocalAgentsSection({ room, store, mention }) {
|
|
2957
|
+
const a = store.actions;
|
|
2958
|
+
const local = room.local || { agents: [] };
|
|
2959
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-local-agents": "", children: [
|
|
2960
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
|
|
2961
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, {}),
|
|
2962
|
+
"Local agents \xB7 ",
|
|
2963
|
+
local.agents.length
|
|
2964
|
+
] }),
|
|
2965
|
+
local.agents.map((ag) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-agent-row", "data-agent-id": ag.agentId, children: [
|
|
2966
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-avatar-wrap", children: [
|
|
2967
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: ag.name, size: 26 }),
|
|
2968
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-dot rm-dot-" + (ag.state === "running" ? "busy" : ag.online ? "online" : "offline"), style: { position: "absolute", right: -1, bottom: -1, border: "2px solid var(--rm-bg)" } })
|
|
2969
|
+
] }),
|
|
2970
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-main", children: [
|
|
2971
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-name", children: [
|
|
2972
|
+
ag.name,
|
|
2973
|
+
ag.moderator ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-agent-tag", children: "moderator" }) : null
|
|
2974
|
+
] }),
|
|
2975
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-sub rm-mono", children: [
|
|
2976
|
+
"@",
|
|
2977
|
+
ag.agentId
|
|
2978
|
+
] }),
|
|
2979
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-sub", children: [
|
|
2980
|
+
ag.scope === "write" ? ag.isolation === "worktree" ? "writes \xB7 own worktree" : ag.isolation === "locks" ? "writes \xB7 file locks" : "writes" : "read-only",
|
|
2981
|
+
ag.approvalRequired ? " \xB7 approval" : "",
|
|
2982
|
+
ag.model ? ` \xB7 ${ag.model}` : "",
|
|
2983
|
+
ag.reasoningEffort ? ` \xB7 ${ag.reasoningEffort}` : ""
|
|
2984
|
+
] })
|
|
2985
|
+
] }),
|
|
2986
|
+
ag.state !== "idle" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: RUN_TONE[ag.state] || "neutral", children: ag.state }) : null,
|
|
2987
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-member-actions", children: [
|
|
2988
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `@mention ${ag.name}`, onClick: () => mention(ag.agentId), children: "@" }),
|
|
2989
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `Edit ${ag.name}`, "aria-label": `Edit ${ag.name}`, onClick: () => a.openDialog({ kind: "agent", roomId: room.roomId, agentId: ag.agentId }), children: "\u270E" })
|
|
2990
|
+
] })
|
|
2991
|
+
] }, ag.agentId)),
|
|
2992
|
+
!local.agents.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Host several agents from this dsh as separate members \u2014 each with its own identity, session and scope." }) : null,
|
|
2993
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}), onClick: () => a.openDialog({ kind: "agent", roomId: room.roomId }), children: "Add local agent" }) })
|
|
2994
|
+
] });
|
|
2995
|
+
}
|
|
2996
|
+
function NumberSetting({ label, value, min, max, onSave, hint }) {
|
|
2997
|
+
const [v, setV] = (0, import_react.useState)(String(value));
|
|
2998
|
+
(0, import_react.useEffect)(() => setV(String(value)), [value]);
|
|
2999
|
+
const commit = () => {
|
|
3000
|
+
const n = Number(v);
|
|
3001
|
+
if (Number.isFinite(n) && n !== value) onSave(Math.min(max, Math.max(min, Math.round(n))));
|
|
3002
|
+
else setV(String(value));
|
|
3003
|
+
};
|
|
3004
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "rm-num-setting", title: hint, children: [
|
|
3005
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label }),
|
|
3006
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "number", min, max, value: v, "aria-label": label, onChange: (e) => setV(e.target.value), onBlur: commit, onKeyDown: (e) => e.key === "Enter" && e.target.blur() })
|
|
3007
|
+
] });
|
|
3008
|
+
}
|
|
3009
|
+
function RunQueue({ room, store, compact }) {
|
|
3010
|
+
const a = store.actions;
|
|
3011
|
+
const runs = room.runs || [];
|
|
3012
|
+
const nameOf = (id) => (room.local && room.local.agents || []).find((x) => x.agentId === id)?.name || id;
|
|
3013
|
+
const active = runs.filter((r) => r.state === "running" || r.state === "queued" || r.state === "approval");
|
|
3014
|
+
const recent = runs.filter((r) => !active.includes(r)).slice(-3).reverse();
|
|
3015
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-runs", "data-rm-runs": "", children: [
|
|
3016
|
+
active.map((r, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-run rm-run-" + r.state, children: [
|
|
3017
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-pos", children: r.state === "running" ? "\u25B6" : r.state === "approval" ? "?" : `#${i + 1 - active.filter((x) => x.state === "running").length}` }),
|
|
3018
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-who", children: nameOf(r.agentId) }),
|
|
3019
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-what", title: r.summary, children: r.summary }),
|
|
3020
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: RUN_TONE[r.state], children: r.state === "approval" ? "needs approval" : r.state })
|
|
3021
|
+
] }, r.id)),
|
|
3022
|
+
!compact ? recent.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-run rm-run-" + r.state, children: [
|
|
3023
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-pos", children: "\u2713" }),
|
|
3024
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-who", children: nameOf(r.agentId) }),
|
|
3025
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-what", title: r.error || r.summary, children: r.error || r.summary }),
|
|
3026
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: RUN_TONE[r.state], children: r.state })
|
|
3027
|
+
] }, r.id)) : null,
|
|
3028
|
+
!active.length && !recent.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "No agent runs yet." }) : null,
|
|
3029
|
+
active.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", className: "rm-danger", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StopIcon, {}), onClick: () => a.stop(room.roomId).then((r) => a.toast(`Stopped ${r.stopped} run(s)`)).catch((e) => a.toast(e.message)), children: "Stop all agents" }) }) : null
|
|
3030
|
+
] });
|
|
3031
|
+
}
|
|
3032
|
+
function OrchestrationSection({ room, store }) {
|
|
3033
|
+
const a = store.actions;
|
|
3034
|
+
const local = room.local;
|
|
3035
|
+
if (!local || !local.agents.length) return null;
|
|
3036
|
+
const save = (patch) => a.saveLocal(room.roomId, patch).catch((e) => a.toast(e.message));
|
|
3037
|
+
const active = (room.runs || []).filter((r) => r.state === "running" || r.state === "queued" || r.state === "approval");
|
|
3038
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-orchestration": "", children: [
|
|
3039
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
|
|
3040
|
+
"Runs \xB7 ",
|
|
3041
|
+
active.filter((r) => r.state === "running").length,
|
|
3042
|
+
"/",
|
|
3043
|
+
local.concurrency,
|
|
3044
|
+
" running",
|
|
3045
|
+
active.length > local.concurrency ? ` \xB7 ${active.length - Math.min(local.concurrency, active.filter((r) => r.state === "running").length)} queued` : ""
|
|
3046
|
+
] }),
|
|
3047
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-num-row", children: [
|
|
3048
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(NumberSetting, { label: "Parallel runs", value: local.concurrency, min: 1, max: 16, onSave: (n) => save({ concurrency: n }), hint: "Max local agent runs at once; the rest wait in the queue." }),
|
|
3049
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(NumberSetting, { label: "Turn limit", value: local.turnLimit, min: 1, max: 100, onSave: (n) => save({ turnLimit: n }), hint: "Max agent-to-agent hand-offs per task/thread. A human message resets it." })
|
|
3050
|
+
] }),
|
|
3051
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RunQueue, { room, store })
|
|
3052
|
+
] });
|
|
3053
|
+
}
|
|
3054
|
+
function IsolationSection({ room, store }) {
|
|
3055
|
+
const a = store.actions;
|
|
3056
|
+
const local = room.local;
|
|
3057
|
+
if (!local || !local.agents.some((x) => x.scope === "write")) return null;
|
|
3058
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-isolation": "", children: [
|
|
3059
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
|
|
3060
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeIcon, {}),
|
|
3061
|
+
"Workspace isolation"
|
|
3062
|
+
] }),
|
|
3063
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { className: "rm-input", "aria-label": "Isolation mode", value: local.isolation, onChange: (e) => a.saveLocal(room.roomId, { isolation: e.target.value }).catch((x) => a.toast(x.message)), children: [
|
|
3064
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "auto", children: "Auto \u2014 git worktree per writing agent, else file locks" }),
|
|
3065
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "worktree", children: "Git worktree per agent" }),
|
|
3066
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "locks", children: "File locks (shared workspace)" }),
|
|
3067
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "off", children: "Off (shared, no locks)" })
|
|
3068
|
+
] }),
|
|
3069
|
+
local.locks && local.locks.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-note", children: [
|
|
3070
|
+
local.locks.length,
|
|
3071
|
+
" file lock(s): ",
|
|
3072
|
+
local.locks.slice(0, 4).map((l) => `${l.path} (${l.owner})`).join(", ")
|
|
3073
|
+
] }) : null,
|
|
3074
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeIcon, {}), onClick: () => a.openDialog({ kind: "merge", roomId: room.roomId }), children: "Review & merge" }) })
|
|
3075
|
+
] });
|
|
3076
|
+
}
|
|
3077
|
+
function TransportSection({ room, store }) {
|
|
3078
|
+
const a = store.actions;
|
|
3079
|
+
const [busy, setBusy] = (0, import_react.useState)(null);
|
|
3080
|
+
const cur = room.transport === "local" ? "local" : room.transport === "lan" ? "relay" : "relay";
|
|
3081
|
+
const go = async (target) => {
|
|
3082
|
+
setBusy(target);
|
|
3083
|
+
try {
|
|
3084
|
+
await a.setTransport(room.roomId, target);
|
|
3085
|
+
a.toast(target === "local" ? "Local only \u2014 in-process, no network" : target === "lan" ? "Now on this machine's LAN relay \u2014 invite remote hosts" : "Now on the relay \u2014 invite remote hosts");
|
|
3086
|
+
} catch (e) {
|
|
3087
|
+
a.toast(e.message);
|
|
3088
|
+
} finally {
|
|
3089
|
+
setBusy(null);
|
|
3090
|
+
}
|
|
3091
|
+
};
|
|
3092
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-transport": "", children: [
|
|
3093
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-detail-h", children: "Transport" }),
|
|
3094
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
|
|
3095
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: !!busy || room.public, className: "rm-mode-btn" + (cur === "local" ? " is-on" : ""), onClick: () => go("local"), title: room.public ? "Public rooms need a relay" : "In-process loopback: no network", children: busy === "local" ? "\u2026" : "Local only" }),
|
|
3096
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: !!busy, className: "rm-mode-btn" + (cur === "relay" && room.transport !== "local" ? " is-on" : ""), onClick: () => go("relay"), children: busy === "relay" ? "\u2026" : "Relay (VPS)" }),
|
|
3097
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: !!busy, className: "rm-mode-btn", onClick: () => go("lan"), title: "Embedded LAN relay on this machine (mDNS)", children: busy === "lan" ? "\u2026" : "LAN" })
|
|
3098
|
+
] }),
|
|
3099
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: room.transport === "local" ? "All members are on this dsh \u2014 nothing leaves the process. Switch to Relay or LAN to invite remote hosts; local agents stay members." : "Local agents connect as separate members over this transport, alongside remote hosts." })
|
|
3100
|
+
] });
|
|
3101
|
+
}
|
|
3102
|
+
function AgentDialog({ store, d }) {
|
|
3103
|
+
const a = store.actions;
|
|
2422
3104
|
const s = useStore(store);
|
|
3105
|
+
const room = (s.rooms || []).find((r) => r.roomId === d.roomId);
|
|
3106
|
+
const existing = room && room.local ? room.local.agents.find((x) => x.agentId === d.agentId) : null;
|
|
3107
|
+
const [presets, setPresets] = (0, import_react.useState)([]);
|
|
3108
|
+
const [f, setF] = (0, import_react.useState)(() => existing ? { name: existing.name, persona: existing.persona || "", systemPrompt: existing.systemPrompt || "", model: existing.model || "", reasoningEffort: existing.reasoningEffort || "medium", scope: existing.scope, approvalRequired: !!existing.approvalRequired, moderator: !!existing.moderator } : { preset: "", name: "", persona: "", systemPrompt: "", model: "", reasoningEffort: "medium", scope: "read", approvalRequired: false, moderator: false });
|
|
3109
|
+
const [err, setErr] = (0, import_react.useState)(null);
|
|
3110
|
+
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
3111
|
+
(0, import_react.useEffect)(() => {
|
|
3112
|
+
a.presets().then(setPresets).catch(() => {
|
|
3113
|
+
});
|
|
3114
|
+
}, []);
|
|
3115
|
+
const pick = (p) => setF({ ...f, preset: p.key, name: p.name, persona: p.persona, systemPrompt: p.systemPrompt, scope: p.scope, moderator: !!p.moderator, reasoningEffort: p.reasoningEffort || "medium" });
|
|
3116
|
+
const submit = async () => {
|
|
3117
|
+
if (!f.name.trim()) {
|
|
3118
|
+
setErr("Name required");
|
|
3119
|
+
return;
|
|
3120
|
+
}
|
|
3121
|
+
setBusy(true);
|
|
3122
|
+
setErr(null);
|
|
3123
|
+
try {
|
|
3124
|
+
const body = { name: f.name, persona: f.persona, systemPrompt: f.systemPrompt, model: f.model, reasoningEffort: f.reasoningEffort, scope: f.scope, approvalRequired: f.approvalRequired, moderator: f.moderator, preset: f.preset || void 0 };
|
|
3125
|
+
if (existing) await a.updateAgent(d.roomId, existing.agentId, body);
|
|
3126
|
+
else {
|
|
3127
|
+
const r = await a.addAgent(d.roomId, body);
|
|
3128
|
+
a.toast(`${r.name} joined as @${r.agentId}`);
|
|
3129
|
+
}
|
|
3130
|
+
a.closeDialog();
|
|
3131
|
+
} catch (e) {
|
|
3132
|
+
setErr(e.message);
|
|
3133
|
+
setBusy(false);
|
|
3134
|
+
}
|
|
3135
|
+
};
|
|
3136
|
+
const remove = async () => {
|
|
3137
|
+
setBusy(true);
|
|
3138
|
+
try {
|
|
3139
|
+
const r = await a.removeAgent(d.roomId, existing.agentId);
|
|
3140
|
+
a.toast(`Removed ${existing.name}${r.branchKept ? ` \xB7 branch ${r.branchKept} kept` : ""}`);
|
|
3141
|
+
a.closeDialog();
|
|
3142
|
+
} catch (e) {
|
|
3143
|
+
setErr(e.message);
|
|
3144
|
+
setBusy(false);
|
|
3145
|
+
}
|
|
3146
|
+
};
|
|
3147
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
3148
|
+
import_dsh_client_ui_primitives.Modal,
|
|
3149
|
+
{
|
|
3150
|
+
open: true,
|
|
3151
|
+
onClose: a.closeDialog,
|
|
3152
|
+
title: existing ? `Edit ${existing.name}` : "Add local agent",
|
|
3153
|
+
closeLabel: "Close",
|
|
3154
|
+
className: "rm-modal-wide",
|
|
3155
|
+
description: existing ? `Member @${existing.agentId} \xB7 own identity and dsh session in this room's workspace.` : "Runs on this dsh as a separate room member: own keypair, own session, own scope. Remote peers see it as a member.",
|
|
3156
|
+
footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
3157
|
+
existing ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", className: "rm-danger", disabled: busy, onClick: remove, children: "Remove" }) : null,
|
|
3158
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { flex: 1 } }),
|
|
3159
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
|
|
3160
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: busy, onClick: submit, children: busy ? "Saving\u2026" : existing ? "Save" : "Add agent" })
|
|
3161
|
+
] }),
|
|
3162
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form rm-form-wide", "data-rm-agent-form": "", children: [
|
|
3163
|
+
!existing ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2", children: [
|
|
3164
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Preset" }),
|
|
3165
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-preset-row", children: presets.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-preset" + (f.preset === p.key ? " is-on" : ""), onClick: () => pick(p), children: [
|
|
3166
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: p.name }),
|
|
3167
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: p.persona })
|
|
3168
|
+
] }, p.key)) })
|
|
3169
|
+
] }) : null,
|
|
3170
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Name", htmlFor: "rm-ag-name", hint: "Member id is derived from it (e.g. coder.ab12).", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-ag-name", value: f.name, maxLength: 40, onChange: (e) => setF({ ...f, name: e.target.value }), placeholder: "e.g. Coder" }) }),
|
|
3171
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Persona", htmlFor: "rm-ag-persona", hint: "One line other members see.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-ag-persona", value: f.persona, maxLength: 200, onChange: (e) => setF({ ...f, persona: e.target.value }), placeholder: "e.g. Implements changes" }) }),
|
|
3172
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "System prompt", htmlFor: "rm-ag-sp", hint: "Prepended to every wake of this agent.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { id: "rm-ag-sp", className: "rm-input rm-textarea", rows: 4, value: f.systemPrompt, maxLength: 4e3, onChange: (e) => setF({ ...f, systemPrompt: e.target.value }) }) }) }),
|
|
3173
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Model", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { className: "rm-input", "aria-label": "Model", value: f.model, onChange: (e) => setF({ ...f, model: e.target.value }), children: [
|
|
3174
|
+
MODEL_CHOICES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: m, children: m || "dsh default model" }, m)),
|
|
3175
|
+
f.model && !MODEL_CHOICES.includes(f.model) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: f.model, children: f.model }) : null
|
|
3176
|
+
] }) }),
|
|
3177
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Reasoning effort", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { className: "rm-input", "aria-label": "Reasoning effort", value: f.reasoningEffort, onChange: (e) => setF({ ...f, reasoningEffort: e.target.value }), children: ["low", "medium", "high", "max"].map((x) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: x, children: x }, x)) }) }),
|
|
3178
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Workspace scope", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
|
|
3179
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.scope === "read" ? " is-on" : ""), onClick: () => setF({ ...f, scope: "read" }), children: "Read-only" }),
|
|
3180
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.scope === "write" ? " is-on" : ""), onClick: () => setF({ ...f, scope: "write" }), children: "Read + write" })
|
|
3181
|
+
] }) }),
|
|
3182
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-col", children: [
|
|
3183
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
|
|
3184
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
3185
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Approval required" }),
|
|
3186
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Each run waits for your OK." })
|
|
3187
|
+
] }),
|
|
3188
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.approvalRequired, onChange: (v) => setF({ ...f, approvalRequired: v }), label: "Approval required" })
|
|
3189
|
+
] }),
|
|
3190
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
|
|
3191
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
3192
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Moderator" }),
|
|
3193
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Splits requests into tasks and summarizes. One per room." })
|
|
3194
|
+
] }),
|
|
3195
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.moderator, onChange: (v) => setF({ ...f, moderator: v }), label: "Moderator" })
|
|
3196
|
+
] })
|
|
3197
|
+
] }),
|
|
3198
|
+
f.scope === "write" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-note", children: "Writes go to its own git worktree (branch room/<room>/<agent>) when the workspace is a git repo; you merge from \u201CReview & merge\u201D. Non-git workspaces use file locks." }) : null,
|
|
3199
|
+
err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-error", children: err }) : null
|
|
3200
|
+
] })
|
|
3201
|
+
}
|
|
3202
|
+
);
|
|
3203
|
+
}
|
|
3204
|
+
function DiffView({ patch }) {
|
|
3205
|
+
if (!patch) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "No changes." });
|
|
3206
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: "rm-diff", "data-rm-diff": "", children: patch.split("\n").map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: l.startsWith("+++") || l.startsWith("---") ? "rm-diff-file" : l.startsWith("+") ? "rm-diff-add" : l.startsWith("-") ? "rm-diff-del" : l.startsWith("@@") ? "rm-diff-hunk" : l.startsWith("diff ") ? "rm-diff-head" : "", children: l || " " }, i)) });
|
|
3207
|
+
}
|
|
3208
|
+
function MergeDialog({ store, d }) {
|
|
3209
|
+
const a = store.actions;
|
|
3210
|
+
const [data, setData] = (0, import_react.useState)(null);
|
|
3211
|
+
const [sel, setSel] = (0, import_react.useState)(null);
|
|
3212
|
+
const [confirm, setConfirm] = (0, import_react.useState)(null);
|
|
3213
|
+
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
3214
|
+
const [result, setResult] = (0, import_react.useState)(null);
|
|
3215
|
+
const load = async () => {
|
|
3216
|
+
try {
|
|
3217
|
+
const r = await a.diffs(d.roomId);
|
|
3218
|
+
setData(r);
|
|
3219
|
+
setSel((cur) => cur && r.agents.some((x) => x.agentId === cur) ? cur : (r.agents.find((x) => x.ahead) || r.agents[0] || {}).agentId || null);
|
|
3220
|
+
} catch (e) {
|
|
3221
|
+
setData({ error: e.message, agents: [] });
|
|
3222
|
+
}
|
|
3223
|
+
};
|
|
3224
|
+
(0, import_react.useEffect)(() => {
|
|
3225
|
+
void load();
|
|
3226
|
+
}, [d.roomId]);
|
|
3227
|
+
const ag = data && data.agents.find((x) => x.agentId === sel);
|
|
3228
|
+
const run = async (kind) => {
|
|
3229
|
+
setBusy(true);
|
|
3230
|
+
setResult(null);
|
|
3231
|
+
try {
|
|
3232
|
+
const r = kind === "merge" ? await a.merge(d.roomId, sel) : await a.discard(d.roomId, sel);
|
|
3233
|
+
setResult(kind === "merge" ? r.merged ? { ok: true, text: `Merged into the main workspace (${r.commit}).` } : { ok: false, text: `${r.error || "Not merged"}${r.conflicts && r.conflicts.length ? ": " + r.conflicts.join(", ") : ""}` } : { ok: true, text: r.discarded ? `Discarded ${r.branch}.` : "Nothing to discard." });
|
|
3234
|
+
await load();
|
|
3235
|
+
} catch (e) {
|
|
3236
|
+
setResult({ ok: false, text: e.message });
|
|
3237
|
+
} finally {
|
|
3238
|
+
setBusy(false);
|
|
3239
|
+
setConfirm(null);
|
|
3240
|
+
}
|
|
3241
|
+
};
|
|
3242
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
3243
|
+
import_dsh_client_ui_primitives.Modal,
|
|
3244
|
+
{
|
|
3245
|
+
open: true,
|
|
3246
|
+
onClose: a.closeDialog,
|
|
3247
|
+
title: "Review & merge agent changes",
|
|
3248
|
+
closeLabel: "Close",
|
|
3249
|
+
className: "rm-modal-wide rm-modal-xl",
|
|
3250
|
+
description: "Each writing agent works on its own branch. Review the diff, then merge into (or discard from) your main workspace. Nothing is merged without your confirmation.",
|
|
3251
|
+
footer: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Close" }),
|
|
3252
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-root rm-merge", "data-rm-merge": "", children: !data ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Loading diffs\u2026" }) : data.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: data.error }) : !data.agents.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "No writing agents in this room." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
3253
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-merge-list", children: data.agents.map((x) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-merge-agent" + (x.agentId === sel ? " is-on" : ""), onClick: () => {
|
|
3254
|
+
setSel(x.agentId);
|
|
3255
|
+
setConfirm(null);
|
|
3256
|
+
setResult(null);
|
|
3257
|
+
}, children: [
|
|
3258
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: x.name, size: 24 }),
|
|
3259
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-merge-agent-body", children: [
|
|
3260
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: x.name }),
|
|
3261
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: x.branch || (x.isolation === "locks" ? "shared workspace \xB7 locks" : "no worktree yet") })
|
|
3262
|
+
] }),
|
|
3263
|
+
x.conflicts && x.conflicts.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "danger", children: "conflict" }) : x.ahead ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_dsh_client_ui_primitives.Tag, { tone: "info", children: [
|
|
3264
|
+
x.files.length,
|
|
3265
|
+
" file",
|
|
3266
|
+
x.files.length === 1 ? "" : "s"
|
|
3267
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "quiet", children: "clean" })
|
|
3268
|
+
] }, x.agentId)) }),
|
|
3269
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-merge-main", children: ag ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
3270
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-merge-head", children: [
|
|
3271
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
3272
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: ag.name }),
|
|
3273
|
+
" ",
|
|
3274
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: ag.branch || "" }),
|
|
3275
|
+
ag.ahead ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-note", style: { margin: "0 0 0 8px" }, children: [
|
|
3276
|
+
ag.ahead,
|
|
3277
|
+
" commit(s) ahead"
|
|
3278
|
+
] }) : null
|
|
3279
|
+
] }),
|
|
3280
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", style: { margin: 0 }, children: confirm ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
3281
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-confirm-text", children: confirm === "merge" ? `Merge ${ag.files.length} file(s) into the main workspace?` : `Discard ${ag.name}'s branch and worktree?` }),
|
|
3282
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: () => setConfirm(null), children: "Cancel" }),
|
|
3283
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "primary", disabled: busy, onClick: () => run(confirm), children: busy ? "Working\u2026" : confirm === "merge" ? "Confirm merge" : "Confirm discard" })
|
|
3284
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
3285
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "ghost", className: "rm-danger", disabled: !ag.branch || busy, onClick: () => setConfirm("discard"), children: "Discard" }),
|
|
3286
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeIcon, {}), disabled: !ag.ahead || busy, onClick: () => setConfirm("merge"), children: "Merge into main" })
|
|
3287
|
+
] }) })
|
|
3288
|
+
] }),
|
|
3289
|
+
result ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: result.ok ? "rm-ok" : "rm-error", children: result.text }) : null,
|
|
3290
|
+
ag.conflicts && ag.conflicts.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-error", children: [
|
|
3291
|
+
"Would conflict with the main workspace: ",
|
|
3292
|
+
ag.conflicts.join(", "),
|
|
3293
|
+
" \u2014 resolve on the branch or discard."
|
|
3294
|
+
] }) : null,
|
|
3295
|
+
ag.mainDirty && ag.mainDirty.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-error", children: [
|
|
3296
|
+
"Uncommitted changes in the main workspace touch: ",
|
|
3297
|
+
ag.mainDirty.join(", ")
|
|
3298
|
+
] }) : null,
|
|
3299
|
+
ag.files && ag.files.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-merge-files", children: ag.files.map((f) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-merge-file", children: [
|
|
3300
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: f.path }),
|
|
3301
|
+
" ",
|
|
3302
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-diff-addn", children: [
|
|
3303
|
+
"+",
|
|
3304
|
+
f.adds
|
|
3305
|
+
] }),
|
|
3306
|
+
" ",
|
|
3307
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-diff-deln", children: [
|
|
3308
|
+
"\u2212",
|
|
3309
|
+
f.dels
|
|
3310
|
+
] })
|
|
3311
|
+
] }, f.path)) }) : null,
|
|
3312
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DiffView, { patch: ag.patch }),
|
|
3313
|
+
ag.truncated ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Diff truncated." }) : null
|
|
3314
|
+
] }) : null })
|
|
3315
|
+
] }) })
|
|
3316
|
+
}
|
|
3317
|
+
);
|
|
3318
|
+
}
|
|
3319
|
+
function EmptyMain({ store }) {
|
|
2423
3320
|
const a = store.actions;
|
|
2424
|
-
|
|
3321
|
+
const workspaces = useNativeWorkspaces(store);
|
|
3322
|
+
const first = workspaces[0];
|
|
3323
|
+
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
3324
|
/* @__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: "
|
|
3325
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: "Rooms live in your workspaces" }),
|
|
3326
|
+
/* @__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
3327
|
/* @__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: [
|
|
3328
|
+
/* @__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
3329
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}),
|
|
2438
3330
|
/* @__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:
|
|
3331
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "New room" }),
|
|
3332
|
+
/* @__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
3333
|
] })
|
|
2442
3334
|
] }),
|
|
2443
3335
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "join" }), children: [
|
|
2444
3336
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkIcon, {}),
|
|
2445
3337
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
2446
3338
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Join by invite" }),
|
|
2447
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Paste a collab
|
|
3339
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Paste a collab://\u2026 invite" })
|
|
2448
3340
|
] })
|
|
2449
3341
|
] }),
|
|
2450
3342
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-welcome-action", onClick: () => a.openDialog({ kind: "browse" }), children: [
|
|
@@ -2461,51 +3353,74 @@ function RoomsMain({ store }) {
|
|
|
2461
3353
|
const s = useStore(store);
|
|
2462
3354
|
const [drafts, setDrafts] = (0, import_react.useState)({});
|
|
2463
3355
|
const room = s.rooms.find((r) => r.roomId === s.selected);
|
|
2464
|
-
if (!room) return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
3356
|
+
if (!room) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-main", "data-collab-main": "", children: [
|
|
3357
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsNav, { store }),
|
|
3358
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-main-body", children: [
|
|
3359
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApprovalBanner, { store }),
|
|
3360
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyMain, { store })
|
|
3361
|
+
] })
|
|
3362
|
+
] });
|
|
2465
3363
|
const messages = s.messages[room.roomId] || [];
|
|
2466
3364
|
const draft = drafts[room.roomId] || "";
|
|
2467
3365
|
const setDraft = (v) => setDrafts((d) => ({ ...d, [room.roomId]: typeof v === "function" ? v(d[room.roomId] || "") : v }));
|
|
2468
3366
|
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
|
-
|
|
3367
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsNav, { store }),
|
|
3368
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-main-body", children: [
|
|
3369
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApprovalBanner, { store }),
|
|
3370
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-workspace", children: [
|
|
3371
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "rm-chat", children: [
|
|
3372
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "rm-chat-header", children: [
|
|
3373
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: room.name, size: 28 }),
|
|
3374
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
3375
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-chat-header-title", children: room.name }),
|
|
3376
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-chat-header-sub", children: [
|
|
3377
|
+
room.members.filter((m) => m.online !== false).length,
|
|
3378
|
+
" online",
|
|
3379
|
+
room.local && room.local.agents.length ? ` \xB7 ${room.local.agents.length} local agent${room.local.agents.length === 1 ? "" : "s"}` : "",
|
|
3380
|
+
" \xB7 ",
|
|
3381
|
+
room.public ? "public" : "private",
|
|
3382
|
+
room.status !== "online" ? ` \xB7 room ${room.status}` : ""
|
|
3383
|
+
] })
|
|
3384
|
+
] }),
|
|
3385
|
+
(() => {
|
|
3386
|
+
const act = (room.runs || []).filter((r) => r.state === "running" || r.state === "queued" || r.state === "approval");
|
|
3387
|
+
if (!act.length) return null;
|
|
3388
|
+
const running = act.filter((r) => r.state === "running").length;
|
|
3389
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-stop-btn", "data-rm-stop": "", title: "Stop all local agent runs", onClick: () => store.actions.stop(room.roomId).then((r) => store.actions.toast(`Stopped ${r.stopped} run(s)`)).catch((e) => store.actions.toast(e.message)), children: [
|
|
3390
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StopIcon, {}),
|
|
3391
|
+
"Stop \xB7 ",
|
|
3392
|
+
running,
|
|
3393
|
+
" running",
|
|
3394
|
+
act.length > running ? `, ${act.length - running} queued` : ""
|
|
3395
|
+
] });
|
|
3396
|
+
})(),
|
|
3397
|
+
room.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge rm-badge-public", children: [
|
|
3398
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
|
|
3399
|
+
"Public"
|
|
3400
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge", children: [
|
|
3401
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}),
|
|
3402
|
+
"Private"
|
|
3403
|
+
] }),
|
|
3404
|
+
room.transport === "local" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge rm-badge-local", children: "Local only" }) : room.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
|
|
3405
|
+
/* @__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
3406
|
] }),
|
|
2490
|
-
|
|
2491
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
3407
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages, members: room.members }),
|
|
3408
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Composer, { room, store, draft, setDraft })
|
|
2492
3409
|
] }),
|
|
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}`);
|
|
3410
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
3411
|
+
RoomDetail,
|
|
3412
|
+
{
|
|
3413
|
+
room,
|
|
3414
|
+
messages,
|
|
3415
|
+
store,
|
|
3416
|
+
mention: (id) => setDraft((d) => `${d}${d && !d.endsWith(" ") ? " " : ""}@${id} `),
|
|
3417
|
+
assign: (id) => {
|
|
3418
|
+
setDraft(`@${id} `);
|
|
3419
|
+
store.actions.toast(`Switch to task mode, then describe the task for @${id}`);
|
|
3420
|
+
}
|
|
2506
3421
|
}
|
|
2507
|
-
|
|
2508
|
-
)
|
|
3422
|
+
)
|
|
3423
|
+
] })
|
|
2509
3424
|
] })
|
|
2510
3425
|
] });
|
|
2511
3426
|
}
|
|
@@ -2516,17 +3431,21 @@ function Field({ label, hint, children, htmlFor }) {
|
|
|
2516
3431
|
hint ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: hint }) : null
|
|
2517
3432
|
] });
|
|
2518
3433
|
}
|
|
2519
|
-
function CreateDialog({ store }) {
|
|
3434
|
+
function CreateDialog({ store, d }) {
|
|
2520
3435
|
const s = useStore(store);
|
|
2521
3436
|
const a = store.actions;
|
|
2522
|
-
const [f, setF] = (0, import_react.useState)({ name: "", description: "", tags: "", public: false, mode: "lan", lanHostPort: "62.234.113.56:80" });
|
|
3437
|
+
const [f, setF] = (0, import_react.useState)({ name: "", description: "", tags: "", public: false, mode: "lan", lanHostPort: "62.234.113.56:80", localOnly: false, agents: [] });
|
|
2523
3438
|
const [err, setErr] = (0, import_react.useState)(null);
|
|
2524
3439
|
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
2525
3440
|
const [lanPeers, setLanPeers] = (0, import_react.useState)([]);
|
|
2526
3441
|
const ok = f.name.trim() && !busy;
|
|
2527
|
-
const ws = (
|
|
3442
|
+
const [ws, setWs] = (0, import_react.useState)(d && d.workspace ? d.workspace : null);
|
|
2528
3443
|
const submit = async () => {
|
|
2529
3444
|
if (!ok) return;
|
|
3445
|
+
if (!ws) {
|
|
3446
|
+
setErr("Pick a workspace");
|
|
3447
|
+
return;
|
|
3448
|
+
}
|
|
2530
3449
|
setBusy(true);
|
|
2531
3450
|
setErr(null);
|
|
2532
3451
|
try {
|
|
@@ -2536,7 +3455,11 @@ function CreateDialog({ store }) {
|
|
|
2536
3455
|
tags: f.tags,
|
|
2537
3456
|
public: f.public,
|
|
2538
3457
|
mode: f.mode,
|
|
2539
|
-
lanHostPort: f.mode === "lan" ? f.lanHostPort.trim() : void 0
|
|
3458
|
+
lanHostPort: f.mode === "lan" ? f.lanHostPort.trim() : void 0,
|
|
3459
|
+
workspacePath: ws.path,
|
|
3460
|
+
workspaceName: ws.title,
|
|
3461
|
+
localOnly: f.localOnly,
|
|
3462
|
+
agents: f.agents
|
|
2540
3463
|
});
|
|
2541
3464
|
} catch (e) {
|
|
2542
3465
|
setErr(e.message);
|
|
@@ -2559,12 +3482,13 @@ function CreateDialog({ store }) {
|
|
|
2559
3482
|
title: "Create room",
|
|
2560
3483
|
closeLabel: "Close",
|
|
2561
3484
|
className: "rm-modal-wide",
|
|
2562
|
-
description: ws ? `In workspace \u201C${ws.
|
|
3485
|
+
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
3486
|
footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
2564
3487
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
|
|
2565
3488
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: !ok, onClick: submit, children: busy ? "Creating\u2026" : "Create" })
|
|
2566
3489
|
] }),
|
|
2567
3490
|
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: [
|
|
3491
|
+
/* @__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
3492
|
/* @__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
3493
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: f.name || "room", size: 36 }),
|
|
2570
3494
|
/* @__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 }) })
|
|
@@ -2585,12 +3509,29 @@ function CreateDialog({ store }) {
|
|
|
2585
3509
|
] }, p.hostPort))
|
|
2586
3510
|
] })
|
|
2587
3511
|
] }) }) : null,
|
|
3512
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-local-box", children: [
|
|
3513
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-switch-row", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
3514
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Local agents" }),
|
|
3515
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Host several agents from this dsh as separate members (own identity, session, scope). Edit them later in room details." })
|
|
3516
|
+
] }) }),
|
|
3517
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-preset-row", children: [["moderator", "Moderator"], ["architect", "Architect"], ["coder", "Coder"], ["reviewer", "Reviewer"]].map(([k, label]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "rm-preset rm-preset-check" + (f.agents.includes(k) ? " is-on" : ""), children: [
|
|
3518
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: f.agents.includes(k), onChange: (e) => setF({ ...f, agents: e.target.checked ? [...f.agents, k] : f.agents.filter((x) => x !== k) }) }),
|
|
3519
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: label })
|
|
3520
|
+
] }, k)) }),
|
|
3521
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
|
|
3522
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
3523
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Local only (no network)" }),
|
|
3524
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "In-process loopback \u2014 nothing leaves this machine. One click to switch to Relay/LAN later." })
|
|
3525
|
+
] }),
|
|
3526
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.localOnly, onChange: (v) => setF({ ...f, localOnly: v, public: v ? false : f.public }), label: "Local only" })
|
|
3527
|
+
] })
|
|
3528
|
+
] }),
|
|
2588
3529
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-switch-row", children: [
|
|
2589
3530
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
2590
3531
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "List in public directory" }),
|
|
2591
3532
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: f.public ? "Anyone can find and join \u2014 key is published with the listing." : "Private: invite only." })
|
|
2592
3533
|
] }),
|
|
2593
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.public, onChange: (v) => setF({ ...f, public: v }), label: "List in public directory" })
|
|
3534
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.public, onChange: (v) => setF({ ...f, public: v, localOnly: v ? false : f.localOnly }), label: "List in public directory" })
|
|
2594
3535
|
] }),
|
|
2595
3536
|
err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-error", children: err }) : null
|
|
2596
3537
|
] })
|
|
@@ -2604,12 +3545,13 @@ function JoinDialog({ store }) {
|
|
|
2604
3545
|
const [err, setErr] = (0, import_react.useState)(null);
|
|
2605
3546
|
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
2606
3547
|
const valid = /^collab:\/\/(join|lan|relay)\/.+#\S+$/.test(invite.trim());
|
|
3548
|
+
const [ws, setWs] = (0, import_react.useState)(null);
|
|
2607
3549
|
const submit = async () => {
|
|
2608
3550
|
if (!valid || busy) return;
|
|
2609
3551
|
setBusy(true);
|
|
2610
3552
|
setErr(null);
|
|
2611
3553
|
try {
|
|
2612
|
-
await a.join(invite.trim());
|
|
3554
|
+
await a.join(invite.trim(), ws ? { workspacePath: ws.path, workspaceName: ws.title } : void 0);
|
|
2613
3555
|
} catch (e) {
|
|
2614
3556
|
setErr(e.message);
|
|
2615
3557
|
setBusy(false);
|
|
@@ -2622,12 +3564,13 @@ function JoinDialog({ store }) {
|
|
|
2622
3564
|
onClose: a.closeDialog,
|
|
2623
3565
|
title: "Join room",
|
|
2624
3566
|
closeLabel: "Close",
|
|
2625
|
-
description: "Paste an invite
|
|
3567
|
+
description: "Paste an invite and pick the dsh workspace (your checkout of the same project) the room attaches to.",
|
|
2626
3568
|
footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
2627
3569
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
|
|
2628
3570
|
/* @__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
3571
|
] }),
|
|
2630
3572
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form", children: [
|
|
3573
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceSelect, { store, value: ws, onChange: setWs }),
|
|
2631
3574
|
/* @__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
3575
|
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
3576
|
err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: err }) : null
|
|
@@ -2817,9 +3760,10 @@ function WorkspaceDialog({ store }) {
|
|
|
2817
3760
|
setBusy(true);
|
|
2818
3761
|
setErr(null);
|
|
2819
3762
|
try {
|
|
2820
|
-
|
|
3763
|
+
if (store.native && store.native.create) await store.native.create({ path: path.trim() });
|
|
3764
|
+
else await a.ensureWorkspace({ name: name2.trim() || void 0, path: path.trim() });
|
|
2821
3765
|
a.closeDialog();
|
|
2822
|
-
a.toast("Workspace
|
|
3766
|
+
a.toast("Workspace added \u2014 it appears in the left sidebar; use New room on it");
|
|
2823
3767
|
} catch (e) {
|
|
2824
3768
|
setErr(e.message);
|
|
2825
3769
|
setBusy(false);
|
|
@@ -2830,18 +3774,14 @@ function WorkspaceDialog({ store }) {
|
|
|
2830
3774
|
{
|
|
2831
3775
|
open: true,
|
|
2832
3776
|
onClose: a.closeDialog,
|
|
2833
|
-
title: "
|
|
3777
|
+
title: "Add workspace",
|
|
2834
3778
|
closeLabel: "Close",
|
|
2835
|
-
description: "
|
|
3779
|
+
description: "Fallback: registers a dsh workspace (same as the native \u201CAdd workspace\u201D). It shows up in the left sidebar.",
|
|
2836
3780
|
footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
2837
3781
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
|
|
2838
3782
|
/* @__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
3783
|
] }),
|
|
2840
3784
|
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
3785
|
/* @__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
3786
|
/* @__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
3787
|
err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: err }) : null
|
|
@@ -2854,7 +3794,7 @@ function BrowseDialog({ store }) {
|
|
|
2854
3794
|
const a = store.actions;
|
|
2855
3795
|
const [joining, setJoining] = (0, import_react.useState)(null);
|
|
2856
3796
|
const inputRef = (0, import_react.useRef)(null);
|
|
2857
|
-
const activeWs = (
|
|
3797
|
+
const [activeWs, setActiveWs] = (0, import_react.useState)(null);
|
|
2858
3798
|
(0, import_react.useEffect)(() => {
|
|
2859
3799
|
a.setSearchOpen(true);
|
|
2860
3800
|
requestAnimationFrame(() => inputRef.current?.focus());
|
|
@@ -2864,7 +3804,7 @@ function BrowseDialog({ store }) {
|
|
|
2864
3804
|
const join = async (r) => {
|
|
2865
3805
|
setJoining(r.roomId);
|
|
2866
3806
|
try {
|
|
2867
|
-
await a.join(r.invite, { name: r.name, description: r.description, tags: r.tags, public: true,
|
|
3807
|
+
await a.join(r.invite, { name: r.name, description: r.description, tags: r.tags, public: true, workspacePath: activeWs?.path, workspaceName: activeWs?.title });
|
|
2868
3808
|
} catch (e) {
|
|
2869
3809
|
a.toast(e.message);
|
|
2870
3810
|
} finally {
|
|
@@ -2879,9 +3819,10 @@ function BrowseDialog({ store }) {
|
|
|
2879
3819
|
title: "Browse public rooms",
|
|
2880
3820
|
closeLabel: "Close",
|
|
2881
3821
|
className: "rm-modal-wide",
|
|
2882
|
-
description: "Directory on VPS 62.234.113.56:80. Search by name or #tag
|
|
3822
|
+
description: "Directory on VPS 62.234.113.56:80. Search by name or #tag; joined rooms attach to the workspace below.",
|
|
2883
3823
|
footer: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Close" }),
|
|
2884
3824
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-browse", children: [
|
|
3825
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceSelect, { store, value: activeWs, onChange: setActiveWs }),
|
|
2885
3826
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-search rm-browse-search", children: [
|
|
2886
3827
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}),
|
|
2887
3828
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -2925,13 +3866,15 @@ function Overlay({ store }) {
|
|
|
2925
3866
|
const s = useStore(store);
|
|
2926
3867
|
const d = s.dialog;
|
|
2927
3868
|
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,
|
|
3869
|
+
d?.kind === "create" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CreateDialog, { store, d }) : null,
|
|
2929
3870
|
d?.kind === "join" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JoinDialog, { store }) : null,
|
|
2930
3871
|
d?.kind === "invite" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InviteDialog, { store, d }) : null,
|
|
2931
3872
|
d?.kind === "leave" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LeaveDialog, { store, d }) : null,
|
|
2932
3873
|
d?.kind === "config" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConfigDialog, { store, d }) : null,
|
|
2933
3874
|
d?.kind === "workspace" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceDialog, { store }) : null,
|
|
2934
3875
|
d?.kind === "browse" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BrowseDialog, { store }) : null,
|
|
3876
|
+
d?.kind === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentDialog, { store, d }) : null,
|
|
3877
|
+
d?.kind === "merge" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeDialog, { store, d }) : null,
|
|
2935
3878
|
s.toast ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Toast, { text: s.toast, onDone: store.actions.clearToast, holdMs: 2200 }) : null
|
|
2936
3879
|
] });
|
|
2937
3880
|
}
|
|
@@ -2944,8 +3887,118 @@ function installStyles() {
|
|
|
2944
3887
|
document.head.appendChild(style);
|
|
2945
3888
|
return () => style.remove();
|
|
2946
3889
|
}
|
|
3890
|
+
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>';
|
|
3891
|
+
function installWorkspaceDecorator(store, native, onNewRoom) {
|
|
3892
|
+
if (typeof document === "undefined" || !native) return () => {
|
|
3893
|
+
};
|
|
3894
|
+
const wsFor = (key) => (native.list.getSnapshot().items || []).find((w) => w.workspaceId === key);
|
|
3895
|
+
const rowKey = (row) => (row.getAttribute("data-row-key") || "").slice("workspace:".length);
|
|
3896
|
+
const open = (key) => {
|
|
3897
|
+
const w = wsFor(key);
|
|
3898
|
+
if (w) onNewRoom({ id: w.workspaceId, path: w.path, title: wsTitle(w) });
|
|
3899
|
+
};
|
|
3900
|
+
const decorate = () => {
|
|
3901
|
+
for (const row of document.querySelectorAll('[data-row-key^="workspace:"]')) {
|
|
3902
|
+
const key = rowKey(row);
|
|
3903
|
+
if (!key || row.querySelector("[data-rm-ws-new-room]")) continue;
|
|
3904
|
+
const newSess = row.querySelector('button[aria-label^="New session in"]');
|
|
3905
|
+
if (!newSess) continue;
|
|
3906
|
+
let anchor = newSess;
|
|
3907
|
+
while (anchor.parentElement && anchor.parentElement !== row && !anchor.parentElement.querySelector('button[aria-label^="Workspace actions"]')) anchor = anchor.parentElement;
|
|
3908
|
+
if (!anchor.parentElement) continue;
|
|
3909
|
+
const label = (newSess.getAttribute("aria-label") || "").replace(/^New session in /, "");
|
|
3910
|
+
const btn = document.createElement("button");
|
|
3911
|
+
btn.type = "button";
|
|
3912
|
+
btn.className = newSess.className;
|
|
3913
|
+
btn.setAttribute("data-rm-ws-new-room", key);
|
|
3914
|
+
btn.setAttribute("aria-label", `New room in ${label}`);
|
|
3915
|
+
btn.title = "New room";
|
|
3916
|
+
btn.innerHTML = ROOM_SVG;
|
|
3917
|
+
btn.addEventListener("click", (e) => {
|
|
3918
|
+
e.preventDefault();
|
|
3919
|
+
e.stopPropagation();
|
|
3920
|
+
open(key);
|
|
3921
|
+
});
|
|
3922
|
+
anchor.parentElement.insertBefore(btn, anchor);
|
|
3923
|
+
}
|
|
3924
|
+
};
|
|
3925
|
+
let pendingKey = null;
|
|
3926
|
+
const onClickCapture = (e) => {
|
|
3927
|
+
const trigger = e.target instanceof Element ? e.target.closest('button[aria-label^="Workspace actions for"]') : null;
|
|
3928
|
+
if (!trigger) return;
|
|
3929
|
+
const row = trigger.closest('[data-row-key^="workspace:"]');
|
|
3930
|
+
pendingKey = row ? rowKey(row) : null;
|
|
3931
|
+
if (!pendingKey) return;
|
|
3932
|
+
const key = pendingKey;
|
|
3933
|
+
let tries = 0;
|
|
3934
|
+
const augment = () => {
|
|
3935
|
+
const menus = [...document.querySelectorAll('[role="menu"]')].filter((m) => m.querySelector('[role="menuitem"]'));
|
|
3936
|
+
const menu = menus[menus.length - 1];
|
|
3937
|
+
if (!menu) {
|
|
3938
|
+
if (++tries < 10) requestAnimationFrame(augment);
|
|
3939
|
+
return;
|
|
3940
|
+
}
|
|
3941
|
+
if (menu.querySelector("[data-rm-ws-menu]")) return;
|
|
3942
|
+
const tpl = menu.querySelector('[role="menuitem"]');
|
|
3943
|
+
const mk = (text, svg, onPick) => {
|
|
3944
|
+
const item = document.createElement("button");
|
|
3945
|
+
item.type = "button";
|
|
3946
|
+
item.setAttribute("role", "menuitem");
|
|
3947
|
+
item.setAttribute("data-rm-ws-menu", text);
|
|
3948
|
+
item.className = tpl.className;
|
|
3949
|
+
item.innerHTML = `<span style="display:inline-flex;width:16px;justify-content:center">${svg}</span><span>${text}</span>`;
|
|
3950
|
+
item.style.display = "flex";
|
|
3951
|
+
item.style.alignItems = "center";
|
|
3952
|
+
item.style.gap = "8px";
|
|
3953
|
+
item.addEventListener("click", (ev) => {
|
|
3954
|
+
ev.preventDefault();
|
|
3955
|
+
ev.stopPropagation();
|
|
3956
|
+
trigger.click();
|
|
3957
|
+
onPick();
|
|
3958
|
+
});
|
|
3959
|
+
return item;
|
|
3960
|
+
};
|
|
3961
|
+
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>';
|
|
3962
|
+
const first = menu.firstElementChild;
|
|
3963
|
+
menu.insertBefore(mk("New session", newSessSvg, () => {
|
|
3964
|
+
const row2 = document.querySelector(`[data-row-key="workspace:${CSS.escape(key)}"]`);
|
|
3965
|
+
row2?.querySelector('button[aria-label^="New session in"]')?.click();
|
|
3966
|
+
}), first);
|
|
3967
|
+
menu.insertBefore(mk("New room", ROOM_SVG, () => open(key)), first);
|
|
3968
|
+
};
|
|
3969
|
+
requestAnimationFrame(augment);
|
|
3970
|
+
};
|
|
3971
|
+
let raf = 0;
|
|
3972
|
+
const schedule = () => {
|
|
3973
|
+
if (!raf) raf = requestAnimationFrame(() => {
|
|
3974
|
+
raf = 0;
|
|
3975
|
+
decorate();
|
|
3976
|
+
});
|
|
3977
|
+
};
|
|
3978
|
+
const mo = new MutationObserver(schedule);
|
|
3979
|
+
mo.observe(document.body, { childList: true, subtree: true });
|
|
3980
|
+
document.addEventListener("click", onClickCapture, true);
|
|
3981
|
+
schedule();
|
|
3982
|
+
return () => {
|
|
3983
|
+
mo.disconnect();
|
|
3984
|
+
document.removeEventListener("click", onClickCapture, true);
|
|
3985
|
+
if (raf) cancelAnimationFrame(raf);
|
|
3986
|
+
for (const b of document.querySelectorAll("[data-rm-ws-new-room]")) b.remove();
|
|
3987
|
+
};
|
|
3988
|
+
}
|
|
2947
3989
|
function apply(ctx) {
|
|
2948
3990
|
const store = createRoomsStore();
|
|
3991
|
+
let native = null;
|
|
3992
|
+
try {
|
|
3993
|
+
native = ctx.get ? ctx.get("workspaces") : ctx.workspaces;
|
|
3994
|
+
} catch {
|
|
3995
|
+
native = ctx.workspaces || null;
|
|
3996
|
+
}
|
|
3997
|
+
store.native = native || null;
|
|
3998
|
+
store.nativeList = native && native.list ? {
|
|
3999
|
+
subscribe: (listener) => native.list.subscribe(listener),
|
|
4000
|
+
getSnapshot: () => native.list.getSnapshot()
|
|
4001
|
+
} : null;
|
|
2949
4002
|
ctx.effect(() => installStyles(), "dsh-room: styles");
|
|
2950
4003
|
ctx.effect(() => () => store.dispose(), "dsh-room: store");
|
|
2951
4004
|
const info = () => ctx.layout.panelInfo;
|
|
@@ -2955,27 +4008,12 @@ function apply(ctx) {
|
|
|
2955
4008
|
} catch {
|
|
2956
4009
|
}
|
|
2957
4010
|
};
|
|
4011
|
+
store.onRoomOpened = ensurePanel;
|
|
2958
4012
|
ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({ name: "sidebar.panellist", id: PANEL_ID, order: 11, label: () => "DSH Room" }, PanelIcon));
|
|
2959
4013
|
ctx.slots.inject("main", () => ctx.slots.register({ name: "main", key: PANEL_ID, inject: () => ({ store }) }, RoomsMain));
|
|
2960
4014
|
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
|
-
});
|
|
4015
|
+
ctx.slots.inject("sidebar.session.row.leading", () => ctx.slots.register({ name: "sidebar.session.row.leading", id: "dsh-room-session-mark", inject: () => ({ store }) }, RoomSessionMark));
|
|
4016
|
+
ctx.effect(() => installWorkspaceDecorator(store, native, (workspace) => store.actions.openDialog({ kind: "create", workspace })), "dsh-room: workspace New room");
|
|
2979
4017
|
}
|
|
2980
4018
|
|
|
2981
4019
|
return module.exports;
|