clay-server 5.1.0-beta.1 → 5.1.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/LICENSES/README.md +4 -0
  2. package/LICENSES/dicebear-avatars.md +16 -0
  3. package/lib/daemon-project-access.js +7 -1
  4. package/lib/daemon.js +2 -0
  5. package/lib/dicebear-avatar.js +62 -0
  6. package/lib/knowledge-db-service.js +175 -0
  7. package/lib/knowledge-db-validation.js +0 -0
  8. package/lib/knowledge-document-index.js +49 -0
  9. package/lib/knowledge-draft-store.js +56 -0
  10. package/lib/knowledge-file-store.js +199 -0
  11. package/lib/knowledge-markdown.js +128 -0
  12. package/lib/mate-knowledge-mcp-server.js +29 -6
  13. package/lib/mate-knowledge-service.js +23 -2
  14. package/lib/mate-knowledge-sync.js +36 -21
  15. package/lib/mate-workspace-color.js +4 -0
  16. package/lib/mates.js +18 -0
  17. package/lib/project-connection.js +36 -2
  18. package/lib/project-debate-proposal.js +6 -0
  19. package/lib/project-debate-workspace.js +43 -0
  20. package/lib/project-debate.js +1 -0
  21. package/lib/project-knowledge.js +315 -136
  22. package/lib/project-mate-creation-proposal.js +1 -0
  23. package/lib/project-sessions.js +39 -2
  24. package/lib/project-user-knowledge.js +60 -0
  25. package/lib/project.js +59 -18
  26. package/lib/public/app.js +73 -34
  27. package/lib/public/clay-studio-wordmark-bold.svg +20 -0
  28. package/lib/public/css/clay-workspace.css +105 -0
  29. package/lib/public/css/debates-workbench.css +47 -0
  30. package/lib/public/css/home-debate-live.css +26 -0
  31. package/lib/public/css/home-hub.css +3 -1
  32. package/lib/public/css/home-mate-model-picker.css +15 -131
  33. package/lib/public/css/home-mate-settings.css +168 -16
  34. package/lib/public/css/icon-strip.css +249 -32
  35. package/lib/public/css/mate-conversation.css +176 -0
  36. package/lib/public/css/mate-knowledge-workbench.css +118 -0
  37. package/lib/public/css/mate-workspace-color.css +16 -0
  38. package/lib/public/css/mates.css +28 -28
  39. package/lib/public/css/messages.css +1 -114
  40. package/lib/public/css/mobile-nav.css +16 -16
  41. package/lib/public/css/overlays.css +8 -0
  42. package/lib/public/css/project-logs.css +7 -0
  43. package/lib/public/css/project-mate-creation.css +105 -0
  44. package/lib/public/css/right-workbench.css +3 -3
  45. package/lib/public/css/session-folders.css +80 -21
  46. package/lib/public/css/sidebar.css +183 -107
  47. package/lib/public/css/title-bar.css +151 -0
  48. package/lib/public/css/worker-delegation.css +47 -0
  49. package/lib/public/css/worker-proposal.css +72 -150
  50. package/lib/public/css/you.css +74 -0
  51. package/lib/public/index.html +33 -101
  52. package/lib/public/modules/account-island.js +230 -0
  53. package/lib/public/modules/app-cursors.js +19 -43
  54. package/lib/public/modules/app-debate-ui.js +4 -3
  55. package/lib/public/modules/app-header.js +106 -0
  56. package/lib/public/modules/app-message-router.js +12 -3
  57. package/lib/public/modules/app-messages.js +34 -9
  58. package/lib/public/modules/app-notifications.js +4 -7
  59. package/lib/public/modules/app-projects.js +5 -38
  60. package/lib/public/modules/app-rendering.js +12 -13
  61. package/lib/public/modules/avatar.js +25 -9
  62. package/lib/public/modules/chat-render-runtime.js +16 -4
  63. package/lib/public/modules/command-palette.js +2 -15
  64. package/lib/public/modules/cursor-sharing-preference.js +3 -3
  65. package/lib/public/modules/debate-controls.js +299 -0
  66. package/lib/public/modules/debate.js +2 -0
  67. package/lib/public/modules/debates-workbench.js +339 -0
  68. package/lib/public/modules/default-ai.js +81 -65
  69. package/lib/public/modules/home-debate-controls.js +18 -289
  70. package/lib/public/modules/home-mate-avatar-editor.js +228 -0
  71. package/lib/public/modules/home-mate-color-picker.js +66 -0
  72. package/lib/public/modules/home-mate-creation.js +1 -1
  73. package/lib/public/modules/home-mate-instructions.js +90 -0
  74. package/lib/public/modules/home-mate-model-picker.js +80 -271
  75. package/lib/public/modules/home-mate-profile-editor.js +72 -0
  76. package/lib/public/modules/home-mate-settings.js +118 -125
  77. package/lib/public/modules/home-shell.js +3 -0
  78. package/lib/public/modules/home-surface-boot.js +1 -9
  79. package/lib/public/modules/icon-strip-tooltip.js +95 -0
  80. package/lib/public/modules/knowledge-db-edit-state.js +35 -0
  81. package/lib/public/modules/knowledge-db-workbench.js +234 -0
  82. package/lib/public/modules/knowledge-history-state.js +55 -0
  83. package/lib/public/modules/knowledge-markdown-editor.js +57 -0
  84. package/lib/public/modules/knowledge-preview-links.js +70 -0
  85. package/lib/public/modules/knowledge-workbench-shell.js +21 -0
  86. package/lib/public/modules/mate-knowledge-workbench-state.js +174 -0
  87. package/lib/public/modules/mate-knowledge-workbench.js +500 -0
  88. package/lib/public/modules/mate-knowledge.js +11 -3
  89. package/lib/public/modules/mate-management.js +54 -10
  90. package/lib/public/modules/mate-sidebar.js +11 -1
  91. package/lib/public/modules/mate-workspace-color.js +33 -0
  92. package/lib/public/modules/mention.js +5 -13
  93. package/lib/public/modules/profile.js +51 -18
  94. package/lib/public/modules/project-chat-identity.js +79 -0
  95. package/lib/public/modules/project-debate-planning-workspace.js +250 -0
  96. package/lib/public/modules/project-debate-workspace.js +185 -0
  97. package/lib/public/modules/project-header-identity.js +127 -0
  98. package/lib/public/modules/project-logs-render.js +9 -4
  99. package/lib/public/modules/project-mate-creation-workspace.js +414 -0
  100. package/lib/public/modules/project-mate-navigation.js +238 -0
  101. package/lib/public/modules/project-session-navigation.js +29 -0
  102. package/lib/public/modules/project-switcher.js +6 -8
  103. package/lib/public/modules/projects-hub-view.js +5 -6
  104. package/lib/public/modules/projects-hub.js +2 -6
  105. package/lib/public/modules/qrcode.js +27 -8
  106. package/lib/public/modules/search-clay-chat.js +8 -11
  107. package/lib/public/modules/session-activity-tooltip.js +12 -0
  108. package/lib/public/modules/session-create-form.js +86 -14
  109. package/lib/public/modules/session-create.js +50 -7
  110. package/lib/public/modules/session-list-visibility.js +41 -0
  111. package/lib/public/modules/session-presence-ui.js +4 -1
  112. package/lib/public/modules/session-title-marquee.js +51 -0
  113. package/lib/public/modules/sidebar-mates.js +3 -50
  114. package/lib/public/modules/sidebar-mobile.js +63 -60
  115. package/lib/public/modules/sidebar-projects.js +84 -97
  116. package/lib/public/modules/sidebar-session-folders-ui.js +2 -2
  117. package/lib/public/modules/sidebar-session-hierarchy.js +149 -70
  118. package/lib/public/modules/sidebar-sessions.js +49 -50
  119. package/lib/public/modules/sidebar.js +11 -13
  120. package/lib/public/modules/thinking-lifecycle.js +7 -6
  121. package/lib/public/modules/tool-palette-order.js +4 -3
  122. package/lib/public/modules/tool-palette.js +13 -3
  123. package/lib/public/modules/tools.js +22 -22
  124. package/lib/public/modules/worker-proposal.js +42 -8
  125. package/lib/public/modules/worker-runtime-picker.js +115 -0
  126. package/lib/public/modules/you-activity.js +121 -0
  127. package/lib/public/modules/you.js +251 -0
  128. package/lib/public/style.css +16 -8
  129. package/lib/public/vendor/codemirror/LICENSE +21 -0
  130. package/lib/public/vendor/codemirror/codemirror.css +344 -0
  131. package/lib/public/vendor/codemirror/codemirror.js +9884 -0
  132. package/lib/public/vendor/codemirror/continuelist.js +101 -0
  133. package/lib/public/vendor/codemirror/markdown.js +886 -0
  134. package/lib/public/vendor/codemirror/show-hint.css +37 -0
  135. package/lib/public/vendor/codemirror/show-hint.js +523 -0
  136. package/lib/sdk-bridge.js +15 -3
  137. package/lib/server-admin.js +11 -0
  138. package/lib/server-home-chat-events.js +1 -1
  139. package/lib/server-home-chat.js +25 -3
  140. package/lib/server-home-debate-planning.js +4 -1
  141. package/lib/server-home-debates.js +2 -0
  142. package/lib/server-home-mate-creation.js +4 -1
  143. package/lib/server-mate-instructions.js +44 -0
  144. package/lib/server-mates.js +12 -3
  145. package/lib/server-palette.js +1 -1
  146. package/lib/server-settings.js +65 -1
  147. package/lib/server-user-knowledge.js +119 -0
  148. package/lib/server.js +21 -1
  149. package/lib/session-visibility.js +7 -2
  150. package/lib/sessions.js +32 -6
  151. package/lib/user-knowledge-service.js +167 -0
  152. package/lib/users-permissions.js +7 -0
  153. package/lib/users-project-mate-filter-preferences.js +39 -0
  154. package/lib/users.js +6 -0
  155. package/lib/workspace-query-access.js +4 -1
  156. package/lib/ws-schema.js +50 -0
  157. package/package.json +10 -1
@@ -56,6 +56,7 @@
56
56
  <!-- === Top Bar (full width, forms reverse-ㄱ with icon strip) === -->
57
57
  <div id="top-bar">
58
58
  <div class="top-bar-left-pills">
59
+ <a class="top-bar-brand-link" href="https://github.com/chadbyte/clay" target="_blank" rel="noopener noreferrer" aria-label="Clay Studio repository"><span class="top-bar-brand-wordmark" aria-hidden="true"></span></a>
59
60
  <button id="pwa-install-pill" class="top-bar-install-btn hidden" title="Open as app"><i data-lucide="download"></i> Open as app</button>
60
61
  <div id="update-pill-wrap" class="top-bar-update hidden">
61
62
  <button id="update-pill" class="top-bar-update-btn"><i data-lucide="arrow-up-circle"></i> <span id="update-version"></span> is available. Update now</button>
@@ -81,114 +82,40 @@
81
82
 
82
83
  <!-- === Icon Strip === -->
83
84
  <div id="icon-strip">
84
- <div class="icon-strip-home" title="Clay Studio">
85
- <span class="icon-strip-pill"></span>
86
- <img class="icon-strip-logo" src="clay-studio-symbol.png" width="38" height="38" alt="Clay Studio">
87
- <span class="icon-strip-home-badge" id="icon-strip-home-badge"></span>
88
- </div>
85
+ <button type="button" class="icon-strip-home icon-strip-brand" aria-label="Open Clay" disabled>
86
+ <img class="icon-strip-logo" src="clay-studio-symbol.png" width="38" height="38" alt="">
87
+ </button>
89
88
  <div class="icon-strip-separator"></div>
90
89
  <button type="button" class="icon-strip-hint" id="icon-strip-hint-project" data-switcher-mode="project" aria-label="Switch project"></button>
90
+ <div class="icon-strip-filter" id="icon-strip-filter">
91
+ <button type="button" class="icon-strip-filter-knob" id="icon-strip-filter-knob" data-mode="all" aria-label="Showing all. Activate to show projects only">
92
+ <span class="icon-strip-filter-face" aria-hidden="true">
93
+ <span class="icon-strip-filter-pointer"></span>
94
+ <span class="icon-strip-filter-category" data-filter-icon="all"><i data-lucide="layout-grid"></i></span>
95
+ <span class="icon-strip-filter-category" data-filter-icon="projects"><i data-lucide="folder"></i></span>
96
+ <span class="icon-strip-filter-category" data-filter-icon="mates"><i data-lucide="users"></i></span>
97
+ </span>
98
+ <span class="icon-strip-filter-label" id="icon-strip-filter-label">All</span>
99
+ </button>
100
+ </div>
91
101
  <div class="icon-strip-projects" id="icon-strip-projects">
92
102
  <div class="skeleton-icon-strip-item"></div>
93
103
  <div class="skeleton-icon-strip-item"></div>
94
104
  </div>
95
- <button class="icon-strip-add" id="icon-strip-add" title="Add project"><i data-lucide="plus"></i></button>
105
+ <button class="icon-strip-add" id="icon-strip-add" type="button" title="Add" aria-label="Add a Mate or project" aria-haspopup="dialog"><i data-lucide="plus"></i></button>
96
106
  <div class="icon-strip-mate-section" id="icon-strip-mate-section">
97
107
  <button type="button" class="icon-strip-hint hidden" id="icon-strip-hint-mate" data-switcher-mode="mate" aria-label="Switch mate"></button>
98
108
  <div class="icon-strip-users hidden" id="icon-strip-users"></div>
99
109
  </div>
100
- <div class="icon-strip-me" id="icon-strip-me"></div>
101
110
  </div>
102
111
 
103
112
  <!-- === Main Area (sidebar + resize-handle + main-column) === -->
104
113
  <div id="main-area">
105
114
  <section id="projects-hub" class="hidden" aria-label="Projects"></section>
106
- <div id="home-hub" class="hidden">
107
- <div class="home-hub-inner">
108
- <button id="home-sidebar-backdrop" class="home-sidebar-backdrop" type="button" aria-label="Close Home navigation"></button>
109
- <aside id="home-sidebar" class="home-sidebar" aria-label="Home navigation">
110
- <div class="home-sidebar-brand-row">
111
- <div class="home-sidebar-brand"><img src="/clay-studio-symbol.png" alt=""><span class="home-sidebar-brand-wordmark">Clay Studio</span></div>
112
- <div class="home-sidebar-brand-actions">
113
- <button id="home-sidebar-all" class="home-sidebar-icon-btn" type="button" title="Search chats" aria-label="Search chats"><i data-lucide="search"></i></button>
114
- <button id="home-sidebar-collapse" class="home-sidebar-icon-btn" type="button" title="Collapse sidebar" aria-label="Collapse Home sidebar"><i data-lucide="panel-left-close"></i></button>
115
- </div>
116
- </div>
117
- <nav class="home-sidebar-primary-actions" aria-label="Home actions">
118
- <button id="home-nav-projects" class="home-sidebar-action" type="button" title="Projects" aria-label="Projects"><span class="home-sidebar-action-icon" aria-hidden="true"><i data-lucide="folder"></i></span><span>Projects</span></button>
119
- <button id="home-sidebar-new" class="home-sidebar-action home-sidebar-new" type="button" title="New Chat" aria-label="Start a new chat with the current Mate" disabled><span class="home-sidebar-action-icon" aria-hidden="true"><i data-lucide="message-square-plus"></i></span><span>New Chat</span></button>
120
- <button id="home-sidebar-debate" class="home-sidebar-action" type="button" title="Browse debates" aria-label="Browse debates" aria-pressed="false"><span class="home-sidebar-action-icon" aria-hidden="true"><i data-lucide="messages-square"></i></span><span>Debates</span></button>
121
- <button id="home-tools-btn" class="home-sidebar-action" type="button" title="Capsules" aria-label="Capsules" aria-expanded="false" aria-controls="home-tool-workbench"><span class="home-sidebar-action-icon" aria-hidden="true"><i data-lucide="box"></i></span><span id="home-tools-label">Capsules</span><span id="home-tools-activity" class="home-tools-activity" aria-hidden="true"></span></button>
122
- </nav>
123
- <section class="home-sidebar-section home-sidebar-mate-section" aria-labelledby="home-sidebar-mate-label">
124
- <button id="home-sidebar-mate-label" class="home-sidebar-label home-sidebar-mate-toggle" type="button" aria-expanded="true" aria-controls="home-mate-list"><span>Mates</span><i data-lucide="chevron-down" aria-hidden="true"></i></button>
125
- <div id="home-mate-list" class="home-mate-list" role="list" aria-label="Mates"></div>
126
- </section>
127
- <section class="home-sidebar-section home-sidebar-recents" aria-labelledby="home-sidebar-recent-label">
128
- <div class="home-sidebar-recents-heading">
129
- <div id="home-sidebar-recent-label" class="home-sidebar-label">Chats</div>
130
- <div class="home-chat-scope" role="group" aria-label="Chat scope">
131
- <button class="home-chat-scope-option" type="button" data-home-chat-scope="current" aria-pressed="false" aria-controls="home-sidebar-recent-list">Current</button>
132
- <button class="home-chat-scope-option" type="button" data-home-chat-scope="all" aria-pressed="true" aria-controls="home-sidebar-recent-list">All</button>
133
- </div>
134
- </div>
135
- <div id="home-sidebar-recent-list" class="home-sidebar-recent-list"></div>
136
- </section>
137
- </aside>
138
- <div id="home-workspace" class="home-workspace">
139
- <button id="home-sidebar-expand" class="home-sidebar-expand" type="button" title="Show sidebar" aria-label="Show Home sidebar" aria-describedby="home-sidebar-expand-brand-label"><i data-lucide="panel-left-open"></i><span id="home-sidebar-expand-brand-label" class="home-sidebar-brand-wordmark home-sidebar-expand-wordmark">Clay Studio</span></button>
140
- <button id="home-close-control" class="home-close-control" type="button" title="Return to project" aria-label="Return to project"><i data-lucide="folder" aria-hidden="true"></i><span>Project</span></button>
141
- <section id="home-project-empty" class="home-project-empty" aria-labelledby="home-project-empty-title" hidden>
142
- <div class="home-project-empty-content">
143
- <span class="home-project-empty-icon" aria-hidden="true"><i data-lucide="folder-plus"></i></span>
144
- <p class="home-project-empty-eyebrow">PROJECT WORKSPACE</p>
145
- <h1 id="home-project-empty-title" tabindex="-1">Start a new project</h1>
146
- <p id="home-project-empty-status" class="home-project-empty-status" role="status">Create an empty project, then continue in its workspace.</p>
147
- <button id="home-project-empty-create" type="button">Start a new project</button>
148
- </div>
149
- </section>
150
- <div id="home-conversation-region" class="home-conversation-region">
151
- <div id="home-mate-chat" class="home-mate-chat">
152
- <div class="home-mate-chat-stage">
153
- <section id="home-debates-archive" class="home-debates-archive" aria-labelledby="home-debates-title" hidden>
154
- <header class="home-debates-header">
155
- <div>
156
- <p class="home-debates-eyebrow">Home archive</p>
157
- <h1 id="home-debates-title" tabindex="-1">Debates</h1>
158
- <p id="home-debates-summary" class="home-debates-summary" role="status" aria-live="polite">Loading debates…</p>
159
- </div>
160
- <button id="home-debates-new" class="home-debates-new" type="button"><i data-lucide="messages-square" aria-hidden="true"></i><span>New debate</span></button>
161
- </header>
162
- <form id="home-debates-topic-form" class="home-debates-topic-form" hidden>
163
- <label for="home-debates-topic">What should the debate be about?</label>
164
- <textarea id="home-debates-topic" rows="2" maxlength="1000" required placeholder="Enter a question or topic"></textarea>
165
- <div class="home-debates-topic-actions">
166
- <p id="home-debates-topic-status" role="status" aria-live="polite">Press Ctrl or Command + Enter to start.</p>
167
- <button id="home-debates-topic-cancel" type="button">Cancel</button>
168
- <button id="home-debates-topic-submit" type="submit">Start debate</button>
169
- </div>
170
- </form>
171
- <div id="home-debates-list" class="home-debates-list" aria-label="Debate archive"></div>
172
- </section>
173
- <div class="home-mate-chat-messages" id="home-mate-chat-messages"></div>
174
- <button id="home-chat-new-activity" class="home-chat-new-activity" type="button" hidden><i data-lucide="arrow-down" aria-hidden="true"></i><span>New activity</span></button>
175
- <div id="home-mate-chat-composer-frame" class="home-mate-chat-composer-frame">
176
- <div id="home-mate-chat-composer" class="home-mate-chat-composer">
177
- <textarea id="home-mate-chat-input" rows="1" placeholder="Message"></textarea>
178
- <button id="home-stt-btn" type="button" aria-label="Start voice input"><i data-lucide="mic"></i></button>
179
- <button id="home-mate-chat-send" aria-label="Send message"><i data-lucide="arrow-up"></i></button>
180
- </div>
181
- <div id="home-mate-chat-session-model" class="home-mate-chat-session-model" role="status" aria-live="polite" title="Model for this conversation: Loading model…">
182
- <i data-lucide="cpu" aria-hidden="true"></i><span>Conversation model</span><span aria-hidden="true">·</span><span id="home-mate-chat-session-model-value">Loading model…</span><button id="home-mate-chat-session-model-choose" class="hidden" type="button">Choose model</button>
183
- </div>
184
- <div id="home-debate-controls-slot" class="home-debate-controls-slot" hidden></div>
185
- </div>
186
- <div id="home-mate-chat-suggestions" class="home-mate-chat-suggestions"></div>
187
- </div>
188
- </div>
189
- </div>
190
- <div id="home-dock-backdrop" class="home-dock-backdrop"></div>
191
- <div id="home-dock-divider" class="home-dock-divider" role="separator" aria-label="Resize Capsules Workbench" aria-orientation="vertical" tabindex="0"></div>
115
+ <!-- Retained inert Capsule host markup. Capsules and their stored data are
116
+ preserved for a future placement without keeping the Home board live. -->
117
+ <template id="retained-capsule-host-template">
118
+ <section id="retained-capsule-host" aria-label="Retained Capsules Workbench">
192
119
  <section id="home-tool-workbench" class="home-tool-workbench" aria-label="Capsules Workbench">
193
120
  <div class="home-dock-header">
194
121
  <button id="home-dock-return" class="home-dock-return" type="button"><i data-lucide="arrow-left"></i><span>Return to conversation</span></button>
@@ -201,15 +128,14 @@
201
128
  </div>
202
129
  <div class="home-dock-content" id="home-dock-content"></div>
203
130
  </section>
204
- </div>
205
- </div>
206
- </div>
131
+ </section>
132
+ </template>
207
133
 
208
134
  <div id="sidebar-column">
209
135
  <div class="title-bar-sidebar">
210
136
  <button class="title-bar-project-dropdown" id="title-bar-project-dropdown">
211
137
  <span class="title-bar-project-icon" id="title-bar-project-icon" aria-hidden="true"></span>
212
- <span class="title-bar-project-name" id="title-bar-project-name"></span>
138
+ <span class="title-bar-project-label"><span class="title-bar-project-name" id="title-bar-project-name"></span><span class="title-bar-project-default" id="title-bar-project-default"></span></span>
213
139
  <i data-lucide="chevron-down" class="title-bar-chevron"></i>
214
140
  </button>
215
141
  <span id="sidebar-presence" class="sidebar-presence"></span>
@@ -655,7 +581,6 @@
655
581
  <div id="mobile-tab-bar">
656
582
  <button class="mobile-tab" data-tab="chat" id="mob-tab-chat"><i data-lucide="message-square"></i><span class="mobile-tab-label">Chat</span></button>
657
583
  <button class="mobile-tab" data-tab="search" id="mob-tab-search"><i data-lucide="search"></i><span class="mobile-tab-label">Search</span></button>
658
- <button class="mobile-tab-home" id="mobile-home-btn"><img src="/clay-studio-symbol.png" alt="Clay Studio home" class="mobile-home-icon"></button>
659
584
  <button class="mobile-tab" data-tab="tools" id="mob-tab-tools"><i data-lucide="wrench"></i><span class="mobile-tab-label">Tools</span></button>
660
585
  <button class="mobile-tab" data-tab="settings" id="mob-tab-settings"><i data-lucide="settings"></i><span class="mobile-tab-label">Settings</span></button>
661
586
  </div>
@@ -941,13 +866,16 @@
941
866
 
942
867
  <!-- === User Island === -->
943
868
  <div id="user-island">
944
- <div class="user-island-profile">
869
+ <button class="user-island-profile" id="user-account-trigger" type="button" aria-label="Open account menu" aria-controls="account-popover" aria-expanded="false">
945
870
  <div class="user-island-avatar"><span class="user-island-avatar-letter">?</span></div>
946
871
  <div class="user-island-info">
947
872
  <span class="user-island-name"></span>
948
873
  <span class="user-island-cta hidden">Personalize your name</span>
949
874
  </div>
950
- </div>
875
+ </button>
876
+ <button id="cursor-share-toggle" class="cursor-share-toggle" type="button" aria-pressed="false" aria-label="Cursor sharing unavailable" disabled>
877
+ <i data-lucide="mouse-pointer-2" class="cursor-share-icon" aria-hidden="true"></i>
878
+ </button>
951
879
  <div class="user-island-actions">
952
880
  <button id="user-settings-btn" title="User settings"><i data-lucide="settings"></i></button>
953
881
  </div>
@@ -1161,7 +1089,7 @@
1161
1089
  <label class="settings-toggle-row">
1162
1090
  <div>
1163
1091
  <span class="settings-label">Mate DMs in projects</span>
1164
- <div class="settings-hint">Show Mate DM shortcuts in project workspaces. Home, Debates, mentions, and Mate memory stay available.</div>
1092
+ <div class="settings-hint">Show Mate DM shortcuts in project workspaces. Debates, mentions, and Mate memory stay available.</div>
1165
1093
  </div>
1166
1094
  <input type="checkbox" id="us-mates-enabled">
1167
1095
  <span class="toggle-track"><span class="toggle-thumb"></span></span>
@@ -2274,6 +2202,10 @@
2274
2202
  <script src="https://cdn.jsdelivr.net/npm/@xterm/addon-fit@0/lib/addon-fit.min.js"></script>
2275
2203
  <script src="https://cdn.jsdelivr.net/npm/@xterm/addon-web-links@0/lib/addon-web-links.min.js"></script>
2276
2204
  <script src="https://cdn.jsdelivr.net/npm/@xterm/addon-webgl@0/lib/addon-webgl.min.js"></script>
2205
+ <script src="vendor/codemirror/codemirror.js"></script>
2206
+ <script src="vendor/codemirror/markdown.js"></script>
2207
+ <script src="vendor/codemirror/continuelist.js"></script>
2208
+ <script src="vendor/codemirror/show-hint.js"></script>
2277
2209
  <script type="module" src="app.js?v=20260913-scheduled-tasks4"></script>
2278
2210
  <div id="pwa-install-modal" class="pwa-modal hidden">
2279
2211
  <div class="pwa-modal-backdrop"></div>
@@ -0,0 +1,230 @@
1
+ import { store } from './store.js';
2
+ import { mountDefaultAi, unmountDefaultAi } from './default-ai.js';
3
+ import { openUserSettings } from './user-settings.js';
4
+ import { getProfile, updateProfileName, regenerateProfileAvatar, showAvatarPositioner, uploadAvatarBlob } from './profile.js';
5
+ import { requestCursorSharingState, setCursorSharing } from './app-cursors.js';
6
+ import { registerTooltip } from './tooltip.js';
7
+
8
+ var popover = null;
9
+ var trigger = null;
10
+ var lastFocus = null;
11
+
12
+ function accountName() { return document.body.dataset.myDisplayName || 'Clay user'; }
13
+
14
+ function close(returnFocus) {
15
+ if (!popover) return;
16
+ var aiSection = popover.querySelector('#account-ai-section');
17
+ if (aiSection) unmountDefaultAi(aiSection);
18
+ popover.remove();
19
+ popover = null;
20
+ trigger.setAttribute('aria-expanded', 'false');
21
+ trigger.setAttribute('aria-label', 'Open account menu for ' + accountName());
22
+ document.removeEventListener('pointerdown', onOutside);
23
+ document.removeEventListener('focusin', onFocusOutside);
24
+ document.removeEventListener('keydown', onKeydown);
25
+ window.removeEventListener('resize', position);
26
+ store.set({ defaultAiInlineEditing: false });
27
+ if (returnFocus && lastFocus && document.contains(lastFocus)) lastFocus.focus({ preventScroll: true });
28
+ lastFocus = null;
29
+ }
30
+
31
+ function isAvatarPositionerTarget(target) { return target && target.closest && target.closest('.avatar-positioner-overlay'); }
32
+ function onOutside(event) { if (!popover || popover.contains(event.target) || trigger.contains(event.target) || isAvatarPositionerTarget(event.target)) return; close(false); }
33
+ function onFocusOutside(event) { if (!popover || popover.contains(event.target) || trigger.contains(event.target) || isAvatarPositionerTarget(event.target)) return; close(false); }
34
+ function onKeydown(event) {
35
+ if (event.key !== 'Escape') return;
36
+ if (isAvatarPositionerTarget(event.target) || document.querySelector('.avatar-positioner-overlay')) return;
37
+ event.preventDefault();
38
+ event.stopPropagation();
39
+ close(true);
40
+ }
41
+
42
+ function position() {
43
+ if (!popover || !trigger) return;
44
+ var rect = trigger.getBoundingClientRect();
45
+ var width = Math.min(280, window.innerWidth - 24);
46
+ var left = Math.max(12, Math.min(window.innerWidth - width - 12, rect.left));
47
+ popover.style.width = width + 'px';
48
+ popover.style.left = left + 'px';
49
+ popover.style.top = Math.max(12, rect.top - popover.offsetHeight - 8) + 'px';
50
+ }
51
+
52
+ function renderNameResting(restoreFocus) {
53
+ var section = popover && popover.querySelector('.account-identity-copy');
54
+ if (!section) return;
55
+ section.innerHTML = '<button type="button" id="account-name-edit" class="account-name-display"><span></span><i data-lucide="pencil" aria-hidden="true"></i></button><small>Personal account</small>';
56
+ section.querySelector('span').textContent = accountName();
57
+ section.querySelector('#account-name-edit').addEventListener('click', startNameEdit);
58
+ if (window.lucide) window.lucide.createIcons({ nodes: [section] });
59
+ if (restoreFocus) section.querySelector('#account-name-edit').focus();
60
+ }
61
+
62
+ function startNameEdit() {
63
+ var section = popover && popover.querySelector('.account-identity-copy');
64
+ if (!section) return;
65
+ var input = document.createElement('input');
66
+ input.id = 'account-profile-name';
67
+ input.setAttribute('aria-label', 'Display name');
68
+ input.className = 'account-name-input';
69
+ input.type = 'text';
70
+ input.maxLength = 50;
71
+ input.autocomplete = 'name';
72
+ input.value = getProfile().name || '';
73
+ section.innerHTML = '';
74
+ section.appendChild(input);
75
+ var hint = document.createElement('small');
76
+ hint.textContent = 'Enter to save · Escape to cancel';
77
+ section.appendChild(hint);
78
+ input.addEventListener('keydown', function(event) {
79
+ if (event.key === 'Enter') {
80
+ event.preventDefault();
81
+ updateProfileName(input.value);
82
+ renderNameResting(true);
83
+ } else if (event.key === 'Escape') {
84
+ event.preventDefault();
85
+ renderNameResting(true);
86
+ }
87
+ event.stopPropagation();
88
+ });
89
+ input.focus();
90
+ input.select();
91
+ }
92
+
93
+ function toggleAvatarEditor() {
94
+ var editor = popover && popover.querySelector('#account-avatar-editor');
95
+ if (!editor) return;
96
+ editor.hidden = !editor.hidden;
97
+ if (!editor.hidden) editor.querySelector('#account-avatar-upload').focus();
98
+ position();
99
+ }
100
+
101
+ function bindAvatarEditor() {
102
+ var avatar = popover.querySelector('#account-avatar-edit');
103
+ var editor = popover.querySelector('#account-avatar-editor');
104
+ var upload = editor.querySelector('#account-avatar-upload');
105
+ var file = editor.querySelector('#account-avatar-file');
106
+ var generate = editor.querySelector('#account-avatar-generate');
107
+ avatar.addEventListener('click', function(event) { event.stopPropagation(); toggleAvatarEditor(); });
108
+ editor.addEventListener('click', function(event) { event.stopPropagation(); });
109
+ upload.addEventListener('click', function() { file.click(); });
110
+ file.addEventListener('change', function() {
111
+ var selected = file.files && file.files[0];
112
+ if (!selected) return;
113
+ var image = new Image();
114
+ var objectUrl = URL.createObjectURL(selected);
115
+ image.onload = function() {
116
+ showAvatarPositioner(image, objectUrl, function(blob) {
117
+ URL.revokeObjectURL(objectUrl);
118
+ uploadAvatarBlob(blob).then(function() { editor.hidden = true; }).catch(function() { editor.dataset.error = 'true'; });
119
+ });
120
+ };
121
+ image.src = objectUrl;
122
+ });
123
+ generate.addEventListener('click', function() { regenerateProfileAvatar(); editor.hidden = true; });
124
+ }
125
+
126
+ function render() {
127
+ popover = document.createElement('div');
128
+ popover.id = 'account-popover';
129
+ popover.className = 'account-popover';
130
+ popover.setAttribute('role', 'dialog');
131
+ popover.setAttribute('aria-label', 'Account');
132
+ var profile = getProfile();
133
+ var identity = document.createElement('div');
134
+ identity.className = 'account-menu-identity';
135
+ identity.innerHTML = '<button type="button" id="account-avatar-edit" class="account-avatar-edit" aria-label="Edit avatar"><img src="' + (document.body.dataset.myAvatarUrl || '') + '" alt=""></button><div class="account-identity-copy"></div><button type="button" class="account-settings-button" aria-label="User Settings" title="User Settings"><i data-lucide="settings" aria-hidden="true"></i></button>';
136
+ identity.querySelector('#account-avatar-edit img').alt = profile.name || 'Clay user';
137
+ var editor = document.createElement('div');
138
+ editor.id = 'account-avatar-editor';
139
+ editor.className = 'account-avatar-editor';
140
+ editor.hidden = true;
141
+ editor.innerHTML = '<button type="button" id="account-avatar-upload">Upload photo</button><button type="button" id="account-avatar-generate">Generate avatar</button><input id="account-avatar-file" type="file" accept="image/*">';
142
+ popover.appendChild(identity);
143
+ popover.appendChild(editor);
144
+ var aiSection = document.createElement('section');
145
+ aiSection.id = 'account-ai-section';
146
+ aiSection.className = 'account-inline-section account-ai-section';
147
+ aiSection.setAttribute('aria-label', 'Default AI');
148
+ popover.appendChild(aiSection);
149
+ mountDefaultAi(aiSection);
150
+ identity.querySelector('.account-settings-button').addEventListener('click', onSettings);
151
+ document.body.appendChild(popover);
152
+ renderNameResting();
153
+ bindAvatarEditor();
154
+ updateCursorAction();
155
+ if (window.lucide) window.lucide.createIcons({ nodes: [popover] });
156
+ position();
157
+ }
158
+
159
+ function updateCursorAction() {
160
+ var button = document.getElementById('cursor-share-toggle');
161
+ if (!button) return;
162
+ var hydrated = store.get('cursorSharingHydrated') === true;
163
+ var enabled = hydrated && store.get('cursorSharingEnabled') === true;
164
+ var pending = store.get('cursorSharingPending') !== null && store.get('cursorSharingPending') !== undefined;
165
+ var connected = store.get('connected') !== false;
166
+ var error = store.get('cursorSharingError');
167
+ var canRetry = !!error && !hydrated && connected;
168
+ var showOff = hydrated && !enabled && !pending && !error && connected;
169
+ button.hidden = store.get('isMultiUserMode') !== true;
170
+ button.removeAttribute('role');
171
+ button.removeAttribute('aria-checked');
172
+ button.setAttribute('aria-pressed', enabled ? 'true' : 'false');
173
+ button.classList.toggle('on', enabled);
174
+ button.classList.toggle('off', showOff);
175
+ button.classList.toggle('pending', pending);
176
+ button.disabled = pending || !connected || (!hydrated && !canRetry);
177
+ var stateLabel = pending ? 'waiting' : !connected ? 'offline' : error ? (canRetry ? 'error; activate to retry' : 'error') : !hydrated ? 'unavailable' : enabled ? 'on' : 'off';
178
+ button.setAttribute('aria-label', 'Cursor sharing ' + stateLabel);
179
+ var tooltipText = pending ? 'Cursor sharing is waiting for confirmation.' : !connected ? 'Cursor sharing is offline.' : error ? (canRetry ? 'Cursor sharing is unavailable. Click to retry.' : 'Cursor sharing is unavailable.') : !hydrated ? 'Cursor sharing is unavailable.' : enabled ? 'Your cursor is visible to others. Click to stop sharing.' : 'Cursor sharing is off. Click to share your cursor.';
180
+ registerTooltip(button, tooltipText);
181
+ }
182
+
183
+ function onSettings(event) { event.stopPropagation(); close(false); openUserSettings(); }
184
+ function onCursorAction(event) {
185
+ event.stopPropagation();
186
+ if (event.currentTarget.disabled) return;
187
+ if (store.get('cursorSharingError') && store.get('cursorSharingHydrated') !== true) {
188
+ requestCursorSharingState();
189
+ return;
190
+ }
191
+ setCursorSharing(!(store.get('cursorSharingEnabled') === true));
192
+ }
193
+ function focusAi() { var control = popover && popover.querySelector('#account-ai-section select'); if (control) control.focus({ preventScroll: true }); }
194
+ function onOpenDefaultAi() { if (!popover) open(); store.set({ defaultAiInlineEditing: true }); focusAi(); }
195
+
196
+ function open() {
197
+ if (popover) { close(true); return; }
198
+ lastFocus = document.activeElement;
199
+ render();
200
+ trigger.setAttribute('aria-expanded', 'true');
201
+ trigger.setAttribute('aria-label', 'Close account menu for ' + accountName());
202
+ document.addEventListener('pointerdown', onOutside);
203
+ document.addEventListener('focusin', onFocusOutside);
204
+ document.addEventListener('keydown', onKeydown);
205
+ window.addEventListener('resize', position);
206
+ }
207
+
208
+ export function initAccountIsland() {
209
+ trigger = document.getElementById('user-account-trigger');
210
+ if (!trigger) return;
211
+ trigger.addEventListener('click', open);
212
+ document.addEventListener('clay:open-account-default-ai', onOpenDefaultAi);
213
+ var cursorToggle = document.getElementById('cursor-share-toggle');
214
+ if (cursorToggle) cursorToggle.addEventListener('click', onCursorAction);
215
+ updateCursorAction();
216
+ trigger.setAttribute('aria-label', 'Open account menu for ' + accountName());
217
+ store.subscribe(function(state, previous) {
218
+ if (state.myUserId !== previous.myUserId || state.connected === false) close(false);
219
+ updateCursorAction();
220
+ if (popover) position();
221
+ });
222
+ document.addEventListener('clay:profile-updated', function() {
223
+ if (!popover) return;
224
+ var identityName = popover.querySelector('.account-name-display span');
225
+ if (identityName) identityName.textContent = accountName();
226
+ var avatar = popover.querySelector('#account-avatar-edit img');
227
+ if (avatar) avatar.src = document.body.dataset.myAvatarUrl || avatar.src;
228
+ });
229
+ window.addEventListener('resize', position);
230
+ }
@@ -199,50 +199,26 @@ function findClosestTurn(clientY) {
199
199
 
200
200
  // Cursor sharing toggle button in user island (multi-user only)
201
201
  export function initCursorToggle() {
202
- if (!store.get('isMultiUserMode')) return;
203
- var actionsEl = document.querySelector(".user-island-actions");
204
- if (!actionsEl) return;
205
- if (document.getElementById("cursor-share-toggle")) return;
206
-
207
- var btn = document.createElement("button");
208
- btn.id = "cursor-share-toggle";
209
- btn.className = "cursor-share-btn";
210
- btn.innerHTML = '<i data-lucide="mouse-pointer-2"></i>';
211
- var settingsBtn = document.getElementById("user-settings-btn");
212
- if (settingsBtn) {
213
- actionsEl.insertBefore(btn, settingsBtn);
214
- } else {
215
- actionsEl.appendChild(btn);
216
- }
217
-
218
- function updateToggleStyle() {
219
- if (store.get("cursorSharingHydrated") && store.get("cursorSharingEnabled")) {
220
- btn.classList.remove("off");
221
- btn.classList.add("on");
222
- registerTooltip(btn, "Cursor sharing on");
223
- } else {
224
- btn.classList.remove("on");
225
- btn.classList.add("off");
226
- registerTooltip(btn, "Cursor sharing off");
227
- }
228
- }
229
-
230
- store.subscribe(function (state, prev) {
231
- if (state.cursorSharingEnabled !== prev.cursorSharingEnabled || state.cursorSharingHydrated !== prev.cursorSharingHydrated) {
232
- updateToggleStyle();
233
- }
234
- });
202
+ // The labeled rail control is owned by account-island.js.
203
+ return;
204
+ }
235
205
 
236
- updateToggleStyle();
237
- lucide.createIcons({ nodes: [btn] });
206
+ export function setCursorSharing(enabled) {
207
+ if (!store.get('cursorSharingHydrated') || store.get('cursorSharingPending') !== null && store.get('cursorSharingPending') !== undefined) return false;
208
+ if (enabled === true && store.get('isMultiUserMode') !== true) return false;
209
+ var ws = getWs();
210
+ if (!ws || ws.readyState !== 1) return false;
211
+ store.set({ cursorSharingPending: enabled });
212
+ ws.send(JSON.stringify({ type: 'cursor_sharing_set', enabled: enabled === true, requestId: 'cursor-sharing-' + Date.now() }));
213
+ return true;
214
+ }
238
215
 
239
- btn.addEventListener("click", function () {
240
- if (!store.get("cursorSharingHydrated")) return;
241
- var next = !store.get("cursorSharingEnabled");
242
- var ws = getWs();
243
- if (!ws || ws.readyState !== 1) return;
244
- ws.send(JSON.stringify({ type: "cursor_sharing_set", enabled: next, requestId: "cursor-sharing-" + Date.now() }));
245
- });
216
+ export function requestCursorSharingState() {
217
+ var ws = getWs();
218
+ if (!ws || ws.readyState !== 1) return false;
219
+ store.set({ cursorSharingPending: 'refresh' });
220
+ ws.send(JSON.stringify({ type: 'cursor_sharing_get', requestId: 'cursor-sharing-refresh-' + Date.now() }));
221
+ return true;
246
222
  }
247
223
 
248
224
  // --- Exported functions ---
@@ -393,7 +369,7 @@ export function initCursors() {
393
369
  if (cursorThrottleTimer) { clearTimeout(cursorThrottleTimer); cursorThrottleTimer = null; }
394
370
  if (selectionThrottleTimer) { clearTimeout(selectionThrottleTimer); selectionThrottleTimer = null; }
395
371
  lastSelectionKey = "";
396
- store.set({ cursorSharingEnabled: false, cursorSharingHydrated: false, cursorSharingAccountId: null });
372
+ store.set({ cursorSharingEnabled: false, cursorSharingHydrated: false, cursorSharingAccountId: null, cursorSharingPending: null, cursorSharingError: disconnected ? "disconnected" : "account-changed" });
397
373
  if (identityChanged && state.myUserId && state.connected) {
398
374
  var ws = getWs();
399
375
  if (ws && ws.readyState === 1) ws.send(JSON.stringify({ type: "cursor_sharing_get", requestId: "cursor-sharing-refresh-" + Date.now() }));
@@ -6,6 +6,7 @@ import { store } from './store.js';
6
6
  import { getWs } from './ws-ref.js';
7
7
  import { scrollToBottom } from './app-rendering.js';
8
8
  import { exportDebateAsPdf } from './debate.js';
9
+ import { getProjectAssistantPlaceholder } from './project-chat-identity.js';
9
10
 
10
11
  // Module-internal UI var (not in store)
11
12
  var debateHandRaiseOpen = false;
@@ -64,7 +65,7 @@ export function exitDebateConcludeMode() {
64
65
  if (banner) banner.remove();
65
66
  var inputEl = document.getElementById("input");
66
67
  if (inputEl && inputEl._origPlaceholder) {
67
- inputEl.placeholder = inputEl._origPlaceholder;
68
+ inputEl.placeholder = getProjectAssistantPlaceholder(store.snap(), store.get('currentVendor'));
68
69
  delete inputEl._origPlaceholder;
69
70
  }
70
71
  }
@@ -129,7 +130,7 @@ export function exitDebateEndedMode() {
129
130
  if (banner) banner.remove();
130
131
  var inputEl2 = document.getElementById("input");
131
132
  if (inputEl2 && inputEl2._origPlaceholder) {
132
- inputEl2.placeholder = inputEl2._origPlaceholder;
133
+ inputEl2.placeholder = getProjectAssistantPlaceholder(store.snap(), store.get('currentVendor'));
133
134
  delete inputEl2._origPlaceholder;
134
135
  }
135
136
  }
@@ -191,7 +192,7 @@ export function exitDebateFloorMode() {
191
192
  if (banner) banner.remove();
192
193
  var inputEl = document.getElementById("input");
193
194
  if (inputEl && inputEl._origPlaceholder) {
194
- inputEl.placeholder = inputEl._origPlaceholder;
195
+ inputEl.placeholder = getProjectAssistantPlaceholder(store.snap(), store.get('currentVendor'));
195
196
  delete inputEl._origPlaceholder;
196
197
  }
197
198
  }