clay-server 5.2.0-beta.10 → 5.2.0-beta.11

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 (98) hide show
  1. package/README.md +0 -4
  2. package/lib/clay-product-control-tools.js +1 -0
  3. package/lib/clay-product-control.js +14 -11
  4. package/lib/clay-vendor-usage.js +44 -0
  5. package/lib/github-star-count.js +147 -0
  6. package/lib/notes-checklist.js +43 -1
  7. package/lib/notes.js +2 -1
  8. package/lib/project-session-notes.js +33 -8
  9. package/lib/project-sessions.js +63 -24
  10. package/lib/project-start-protocol.js +29 -0
  11. package/lib/project-vendor-login.js +2 -0
  12. package/lib/project.js +3 -0
  13. package/lib/public/app.js +25 -17
  14. package/lib/public/css/contextual-clay.css +1 -1
  15. package/lib/public/css/filebrowser.css +1 -1
  16. package/lib/public/css/mate-loading-scene.css +21 -0
  17. package/lib/public/css/overlays.css +64 -256
  18. package/lib/public/css/project-mate-creation.css +5 -6
  19. package/lib/public/css/project-start.css +108 -0
  20. package/lib/public/css/projects-hub.css +53 -14
  21. package/lib/public/css/settings-shell.css +103 -0
  22. package/lib/public/css/sidebar.css +7 -0
  23. package/lib/public/css/title-bar.css +79 -0
  24. package/lib/public/index.html +50 -63
  25. package/lib/public/modules/admin-settings-invitations.js +2 -2
  26. package/lib/public/modules/admin-settings-projects.js +2 -2
  27. package/lib/public/modules/admin-settings-ui.js +20 -4
  28. package/lib/public/modules/admin-settings-users.js +4 -4
  29. package/lib/public/modules/app-home-hub.js +2 -1
  30. package/lib/public/modules/app-messages.js +10 -2
  31. package/lib/public/modules/app-projects.js +1 -407
  32. package/lib/public/modules/app-rate-limit.js +12 -1
  33. package/lib/public/modules/clay-tool-presentation.js +1 -0
  34. package/lib/public/modules/contextual-clay.js +3 -1
  35. package/lib/public/modules/github-star.js +66 -0
  36. package/lib/public/modules/mate-loading-scene.js +17 -0
  37. package/lib/public/modules/profile.js +11 -3
  38. package/lib/public/modules/project-mate-creation-workspace.js +13 -4
  39. package/lib/public/modules/project-session-visibility-settings.js +19 -16
  40. package/lib/public/modules/project-settings.js +119 -129
  41. package/lib/public/modules/project-start-dialog.js +130 -0
  42. package/lib/public/modules/project-start-folder-state.js +78 -0
  43. package/lib/public/modules/project-start-folder-view.js +190 -0
  44. package/lib/public/modules/project-start-forms.js +66 -0
  45. package/lib/public/modules/project-start-path.js +60 -0
  46. package/lib/public/modules/project-start-state.js +54 -0
  47. package/lib/public/modules/project-start-submit.js +67 -0
  48. package/lib/public/modules/projects-hub-view.js +33 -14
  49. package/lib/public/modules/projects-hub.js +10 -6
  50. package/lib/public/modules/server-settings.js +36 -91
  51. package/lib/public/modules/settings-project-target.js +56 -0
  52. package/lib/public/modules/settings-select.js +1 -1
  53. package/lib/public/modules/settings-shell-registry.js +88 -0
  54. package/lib/public/modules/settings-shell-view.js +104 -0
  55. package/lib/public/modules/settings-shell.js +342 -0
  56. package/lib/public/modules/sidebar-mobile.js +8 -30
  57. package/lib/public/modules/sidebar-projects.js +2 -1
  58. package/lib/public/modules/theme.js +10 -2
  59. package/lib/public/modules/user-settings-layout.js +8 -33
  60. package/lib/public/modules/user-settings-profile.js +1 -1
  61. package/lib/public/modules/user-settings.js +18 -85
  62. package/lib/public/modules/vendor-usage-format.js +93 -0
  63. package/lib/public/modules/vendor-usage-list.js +45 -0
  64. package/lib/public/modules/vendor-usage-model.js +215 -0
  65. package/lib/public/modules/vendor-usage-overview.js +312 -0
  66. package/lib/public/style.css +10 -7
  67. package/lib/public/welcome-art/conversation-desk.png +0 -0
  68. package/lib/public/welcome-art/mate-clay.png +0 -0
  69. package/lib/public/welcome-art/project-clay.png +0 -0
  70. package/lib/sdk-bridge.js +6 -1
  71. package/lib/sdk-message-processor.js +9 -1
  72. package/lib/sdk-quota-snapshot.js +20 -2
  73. package/lib/server-github-stars.js +39 -0
  74. package/lib/server-global-ws.js +21 -17
  75. package/lib/server.js +4 -0
  76. package/lib/session-notes-mcp-server.js +26 -4
  77. package/lib/session-notes-writes.js +88 -0
  78. package/lib/session-visibility.js +20 -8
  79. package/lib/sessions.js +2 -0
  80. package/lib/vendor-usage-acp-readers.js +97 -0
  81. package/lib/vendor-usage-capabilities.js +88 -0
  82. package/lib/vendor-usage-http-readers.js +214 -0
  83. package/lib/vendor-usage-metrics.js +47 -0
  84. package/lib/vendor-usage-native-readers.js +106 -0
  85. package/lib/vendor-usage-overview.js +406 -0
  86. package/lib/vendor-usage-service.js +281 -0
  87. package/lib/ws-schema.js +4 -2
  88. package/lib/yoke/adapters/claude.js +12 -0
  89. package/lib/yoke/adapters/codex-query-contract.js +1 -0
  90. package/lib/yoke/adapters/codex.js +45 -12
  91. package/lib/yoke/claude-hook-installer.js +1 -0
  92. package/lib/yoke/codex-dynamic-tool-names.js +80 -0
  93. package/lib/yoke/codex-model-catalog.js +37 -3
  94. package/lib/yoke/muse-services.js +16 -3
  95. package/lib/yoke/providers/claude-model-descriptions.js +22 -2
  96. package/lib/yoke/vendor-registry.js +6 -2
  97. package/package.json +2 -1
  98. package/lib/public/modules/server-settings-navigation.js +0 -39
@@ -0,0 +1,103 @@
1
+ /* Unified full-screen settings shell. Personal, Project and Server panes keep
2
+ their ids (so the shared settings-ui primitives still apply) and mount into
3
+ the shell content; their former modal chrome is neutralised here. */
4
+ #settings-shell {
5
+ position: fixed; inset: 0; z-index: 600; display: flex; flex-direction: column;
6
+ background: var(--bg); color: var(--text);
7
+ }
8
+ #settings-shell.hidden { display: none; }
9
+ #settings-shell [hidden], #settings-shell .hidden { display: none !important; }
10
+ #settings-shell :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
11
+
12
+ /* --- Header: close and scope tabs --- */
13
+ .settings-shell-header {
14
+ flex: 0 0 auto; display: flex; align-items: center; gap: 12px; min-height: 48px;
15
+ padding: calc(var(--safe-top) + 6px) 16px 6px; border-bottom: 1px solid var(--border-subtle); background: var(--sidebar-bg);
16
+ }
17
+ .settings-shell-close {
18
+ display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 10px 0 8px; border: 1px solid var(--border);
19
+ border-radius: 8px; background: var(--bg); color: var(--text-secondary); font: inherit; font-size: 13px; cursor: pointer;
20
+ }
21
+ .settings-shell-close:hover { color: var(--text); border-color: var(--text-dimmer); }
22
+ .settings-shell-close svg { width: 16px; height: 16px; }
23
+ .settings-shell-close kbd { font: inherit; font-size: 11px; color: var(--text-dimmer); padding-left: 4px; }
24
+ .settings-scope-tabs { display: inline-flex; gap: 2px; padding: 3px; border: 1px solid var(--border); border-radius: 9px; background: var(--bg-alt); }
25
+ .settings-scope-tab {
26
+ min-width: 76px; height: 28px; padding: 0 12px; border: 0; border-radius: 6px; background: none;
27
+ color: var(--text-muted); font: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
28
+ }
29
+ .settings-scope-tab:hover { color: var(--text); }
30
+ .settings-scope-tab.active { background: var(--bg); color: var(--text); box-shadow: 0 1px 3px rgba(0, 0, 0, .12); }
31
+
32
+ /* --- Body: navigation + content --- */
33
+ .settings-shell-body { flex: 1 1 auto; min-height: 0; display: flex; }
34
+ .settings-shell-nav { flex: 0 0 240px; width: 240px; overflow-y: auto; background: var(--sidebar-bg); border-right: 1px solid var(--border-subtle); }
35
+ .settings-shell-nav-inner { padding: 20px 12px 32px 14px; }
36
+ .settings-scope-head { display: flex; flex-direction: column; gap: 2px; padding: 0 10px 12px; margin-bottom: 8px; border-bottom: 1px solid var(--border-subtle); }
37
+ .settings-scope-head strong { font-size: 14px; color: var(--text); }
38
+ .settings-scope-head small { font-size: 12px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
39
+ .settings-scope-hint { font-size: 12px; line-height: 1.35; color: var(--text-muted); }
40
+ .settings-project-picker { padding: 0 4px 12px; }
41
+ .settings-project-picker .settings-label { display: block; margin: 0 0 6px 6px; font-size: 12px; color: var(--text-muted); }
42
+ .settings-project-picker .settings-picker { display: block; width: 100%; }
43
+ .settings-project-picker .settings-picker-menu { right: auto; left: 0; width: 100%; max-width: none; }
44
+ .settings-project-picker select { width: 100%; }
45
+ .settings-shell-nav .settings-nav-item { min-height: 32px; padding: 6px 10px; font-size: 13px; }
46
+ .settings-shell-nav .settings-nav-item.active { background: rgba(var(--overlay-rgb), .08); color: var(--text); }
47
+ .settings-shell-nav .settings-nav-item:focus-visible { outline-offset: -2px; }
48
+ .settings-shell-nav .settings-nav-dropdown { display: none; }
49
+ .settings-shell-content { flex: 1 1 auto; min-width: 0; overflow-y: auto; padding: 28px 40px calc(var(--safe-bottom) + 56px); }
50
+ .settings-shell-status { max-width: 760px; margin: 0 0 16px; padding: 8px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-alt); color: var(--text-secondary); font-size: 13px; }
51
+ .settings-shell-status[data-state="error"] { border-color: color-mix(in srgb, var(--error) 40%, var(--border)); color: var(--error); }
52
+
53
+ /* --- Panes: neutralise the former modal/overlay chrome --- */
54
+ #settings-shell .settings-pane { position: static; inset: auto; z-index: auto; display: block; width: 100%; max-width: 760px; margin: 0; padding: 0; background: none; backdrop-filter: none; animation: none; overflow: visible; }
55
+ #settings-shell .settings-pane.hidden { display: none !important; }
56
+ #user-settings .us-modal, #project-settings .ps-modal {
57
+ position: static; display: block; width: auto; height: auto; border: 0; border-radius: 0; background: none; box-shadow: none; animation: none; overflow: visible;
58
+ }
59
+ #user-settings .us-modal-body, #project-settings .project-settings-layout { display: block; height: auto; overflow: visible; background: none; }
60
+ #user-settings .us-modal-content, #project-settings .project-settings-content, #server-settings .server-settings-content {
61
+ max-width: none; padding: 0; background: none; overflow: visible;
62
+ }
63
+ #project-settings .project-settings-content-inner, #server-settings .server-settings-content-inner { padding: 0; }
64
+ #server-settings .server-settings-layout { display: block; height: auto; background: none; }
65
+ #server-settings .server-settings-layout::after { display: none; }
66
+ #project-settings .ps-section h2, #user-settings .us-section h2, #server-settings .server-settings-section h2 { margin-top: 0; }
67
+ #project-settings.ps-no-target .ps-section { display: none; }
68
+ #project-settings .settings-empty { padding: 24px 0; }
69
+
70
+ /* --- Inline subviews (admin editors, avatar crop): replace the page's list in place --- */
71
+ .has-subview > :not(h2):not(.settings-subview) { display: none !important; }
72
+ .admin-subview, .avatar-positioner-inline { display: block; position: static; padding: 0; background: none; }
73
+ .admin-subview .admin-modal {
74
+ position: static; width: auto; max-width: 640px; max-height: none; margin: 0; border: 1px solid var(--border);
75
+ border-radius: 10px; background: var(--bg); box-shadow: none;
76
+ }
77
+ .admin-subview .admin-modal-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid var(--border-subtle); }
78
+ .admin-subview .admin-modal-header h3 { margin: 0; font-size: 14px; }
79
+ .admin-subview .admin-modal-body { padding: 14px 16px; overflow: visible; max-height: none; }
80
+ .admin-subview .admin-modal-footer { display: flex; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--border-subtle); }
81
+ .avatar-positioner-inline .avatar-positioner-container { position: relative; display: flex; flex-direction: column; align-items: center; gap: 12px; max-width: 360px; padding: 16px; border: 1px solid var(--border); border-radius: 10px; background: var(--bg); box-shadow: none; }
82
+
83
+ /* --- Narrow screens: tabs in the header, page select replaces the nav list --- */
84
+ @media (max-width: 800px) {
85
+ .settings-shell-header { padding-left: 10px; padding-right: 10px; gap: 8px; }
86
+ .settings-shell-close span, .settings-shell-close kbd { display: none; }
87
+ .settings-shell-close { width: 32px; padding: 0; justify-content: center; }
88
+ .settings-scope-tabs { flex: 1 1 auto; min-width: 0; }
89
+ .settings-scope-tab { flex: 1 1 0; min-width: 0; padding: 0 6px; }
90
+ .settings-shell-body { flex-direction: column; }
91
+ .settings-shell-nav { flex: 0 0 auto; width: 100%; max-height: 40%; border-right: 0; border-bottom: 1px solid var(--border-subtle); }
92
+ .settings-shell-nav-inner { padding: 10px 12px; display: grid; gap: 8px; }
93
+ .settings-scope-head { display: none; }
94
+ .settings-project-picker { padding: 0; }
95
+ .settings-project-picker .settings-picker { display: none; }
96
+ .settings-project-picker select { height: 36px; }
97
+ .settings-shell-nav-items { display: none; }
98
+ .settings-shell-nav .settings-nav-dropdown { display: block; width: 100%; height: 36px; margin: 0; }
99
+ .settings-shell-content { padding: 18px 14px calc(var(--safe-bottom) + 40px); }
100
+ }
101
+ @media (prefers-reduced-motion: reduce) {
102
+ #settings-shell * { transition: none !important; animation: none !important; }
103
+ }
@@ -1392,6 +1392,13 @@ body:not(.home-active) #icon-strip-hint-mate { display: none; }
1392
1392
  .user-island-profile:active { transform: translateY(1px); }
1393
1393
  .user-island-profile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
1394
1394
 
1395
+ .user-island-quick-controls {
1396
+ display: flex;
1397
+ align-items: center;
1398
+ justify-content: center;
1399
+ gap: 4px;
1400
+ }
1401
+
1395
1402
  .cursor-share-toggle {
1396
1403
  position: relative;
1397
1404
  width: var(--account-rail-cursor-target, 28px);
@@ -906,3 +906,82 @@
906
906
  .rate-limit-popover { font-size: 11px; padding: 6px 10px; }
907
907
  .header-context { width: 50px; }
908
908
  }
909
+
910
+ /* --- GitHub Star link (desktop only, beside the brand) --- */
911
+ .top-bar-star {
912
+ display: flex;
913
+ align-items: center;
914
+ gap: 6px;
915
+ flex: 0 0 auto;
916
+ white-space: nowrap;
917
+ }
918
+
919
+ .top-bar-star-pill {
920
+ display: inline-flex;
921
+ align-items: stretch;
922
+ height: 20px;
923
+ border: 1px solid var(--border-subtle);
924
+ border-radius: 6px;
925
+ background: rgba(var(--overlay-rgb), 0.03);
926
+ color: var(--text-muted);
927
+ font-size: 11px;
928
+ font-weight: 600;
929
+ line-height: 18px;
930
+ text-decoration: none;
931
+ overflow: hidden;
932
+ transition: background 0.15s, border-color 0.15s, color 0.15s;
933
+ }
934
+
935
+ .top-bar-star-pill:hover {
936
+ background: rgba(var(--overlay-rgb), 0.08);
937
+ border-color: var(--border);
938
+ color: var(--text-secondary);
939
+ }
940
+
941
+ .top-bar-star-pill:active { transform: translateY(1px); }
942
+
943
+ .top-bar-star-pill:focus-visible {
944
+ outline: 2px solid var(--accent);
945
+ outline-offset: 2px;
946
+ }
947
+
948
+ .top-bar-star-mark {
949
+ align-self: center;
950
+ width: 12px;
951
+ height: 12px;
952
+ margin: 0 4px 0 6px;
953
+ fill: currentColor;
954
+ }
955
+
956
+ .top-bar-star-label { padding-right: 6px; }
957
+
958
+ .top-bar-star-count {
959
+ padding: 0 6px;
960
+ border-left: 1px solid var(--border-subtle);
961
+ font-variant-numeric: tabular-nums;
962
+ }
963
+
964
+ .top-bar-star-count.hidden { display: none; }
965
+
966
+ @media (max-width: 790px) {
967
+ .top-bar-star-long { display: none; }
968
+ }
969
+
970
+ /* The CTA yields to functional controls sharing the left group: the install
971
+ pill shortens the label first (and takes the pill when space is tight); a
972
+ visible update notice takes the whole CTA. */
973
+ .top-bar-left-pills:has(.top-bar-install-btn:not(.hidden)) .top-bar-star-long { display: none; }
974
+ .top-bar-left-pills:has(#update-pill-wrap:not(.hidden)) .top-bar-star { display: none; }
975
+
976
+ @media (max-width: 1100px) {
977
+ .top-bar-left-pills:has(.top-bar-install-btn:not(.hidden)) .top-bar-star { display: none; }
978
+ }
979
+
980
+ @media (max-width: 768px) {
981
+ .top-bar-star { display: none; }
982
+ }
983
+
984
+ @media (prefers-reduced-motion: reduce) {
985
+ .top-bar-star-pill { transition: none; }
986
+ .top-bar-star-pill:active { transform: none; }
987
+ }
@@ -60,6 +60,9 @@ body { background: transparent; color: inherit; }
60
60
  <div id="top-bar">
61
61
  <div class="top-bar-left-pills">
62
62
  <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>
63
+ <div class="top-bar-star" id="github-star">
64
+ <a class="top-bar-star-pill" id="github-star-link" href="https://github.com/chadbyte/clay" target="_blank" rel="noopener noreferrer" aria-label="Star Clay on GitHub"><svg class="top-bar-star-mark" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg><span class="top-bar-star-label">Star<span class="top-bar-star-long"> on GitHub</span></span><span class="top-bar-star-count hidden" id="github-star-count"></span></a>
65
+ </div>
63
66
  <button id="pwa-install-pill" class="top-bar-install-btn hidden" title="Open as app"><i data-lucide="download"></i> Open as app</button>
64
67
  <div id="update-pill-wrap" class="top-bar-update hidden">
65
68
  <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>
@@ -74,9 +77,8 @@ body { background: transparent; color: inherit; }
74
77
  <!-- Pill badges -->
75
78
  <div id="skip-perms-pill" class="top-bar-pill pill-error hidden"><i data-lucide="shield-off"></i> <span>Skip perms</span></div>
76
79
  <div id="client-count" class="top-bar-pill pill-accent hidden"><i data-lucide="users"></i> <span id="client-count-text"></span></div>
77
- <button id="share-pill" title="Share"><i data-lucide="share-2"></i></button>
78
80
  <button id="notif-center-btn" title="Notifications"><i data-lucide="bell"></i><span id="notif-center-badge" class="notif-badge hidden">0</span></button>
79
- <button id="server-settings-btn" title="Server settings"><i data-lucide="settings"></i></button>
81
+ <button id="server-settings-btn" title="Settings" aria-label="Settings" aria-expanded="false"><i data-lucide="settings"></i></button>
80
82
  </div>
81
83
  </div>
82
84
 
@@ -657,13 +659,10 @@ body { background: transparent; color: inherit; }
657
659
  </div>
658
660
  </div>
659
661
  <div id="project-settings" class="hidden">
660
- <div class="ps-modal" role="dialog" aria-modal="true" aria-labelledby="ps-modal-title">
661
- <header class="ps-modal-header"><div><h2 id="ps-modal-title">Project settings</h2><span id="ps-nav-title"></span></div><button class="settings-btn-sm" id="project-settings-close" aria-label="Close project settings"><i data-lucide="x"></i></button></header>
662
+ <div class="ps-modal">
662
663
  <div class="project-settings-layout">
663
- <nav class="project-settings-nav" aria-label="Project settings">
664
- <div class="project-settings-nav-inner"><select id="ps-nav-dropdown" class="settings-nav-dropdown" aria-label="Settings page"></select><div class="server-settings-nav-items"></div></div>
665
- </nav>
666
664
  <div class="project-settings-content"><div class="project-settings-content-inner">
665
+ <div class="settings-empty" id="ps-empty" hidden><h2 tabindex="-1">No project selected</h2><p id="ps-empty-text" class="settings-hint"></p></div>
667
666
  <section class="ps-section active" data-section="profile">
668
667
  <h2>General</h2><p class="ps-section-desc">Identify this project in your workspace.</p>
669
668
  <div class="ps-project-identity">
@@ -680,8 +679,8 @@ body { background: transparent; color: inherit; }
680
679
  <div class="settings-field" id="ps-session-visibility-default-field" style="display:none">
681
680
  <label class="settings-label" for="ps-session-visibility-default">New session visibility</label>
682
681
  <div class="settings-hint">Applies to new project conversations and Issue Start work sessions. Existing sessions, hidden work, and delegated workflows keep their current visibility.</div>
682
+ <div class="settings-hint hidden" id="ps-session-visibility-default-owner-hint">Only the project owner can change this setting. Administrators can view it here but cannot change it.</div>
683
683
  <div class="settings-hint hidden" id="ps-session-visibility-default-inherited-hint">This worktree inherits the setting from its parent project. Change it there.</div>
684
- <div class="settings-hint hidden" id="ps-session-visibility-default-non-current-hint">Open this project to change this setting.</div>
685
684
  <select class="ps-select" id="ps-session-visibility-default">
686
685
  <option value="private">Private</option>
687
686
  <option value="shared">Shared with project members</option>
@@ -737,11 +736,16 @@ body { background: transparent; color: inherit; }
737
736
  <span class="user-island-cta hidden">Personalize your name</span>
738
737
  </div>
739
738
  </button>
740
- <button id="cursor-share-toggle" class="cursor-share-toggle" type="button" aria-pressed="false" aria-label="Cursor sharing unavailable" disabled>
741
- <i data-lucide="mouse-pointer-2" class="cursor-share-icon" aria-hidden="true"></i>
742
- </button>
739
+ <div class="user-island-quick-controls">
740
+ <button id="cursor-share-toggle" class="cursor-share-toggle" type="button" aria-pressed="false" aria-label="Cursor sharing unavailable" disabled>
741
+ <i data-lucide="mouse-pointer-2" class="cursor-share-icon" aria-hidden="true"></i>
742
+ </button>
743
+ <button id="appearance-mode-toggle" class="cursor-share-toggle" type="button" aria-label="Switch to light mode" title="Switch to light mode">
744
+ <i data-lucide="sun" class="cursor-share-icon" aria-hidden="true"></i>
745
+ </button>
746
+ </div>
743
747
  <div class="user-island-actions">
744
- <button id="user-settings-btn" title="User settings"><i data-lucide="settings"></i></button>
748
+ <button id="user-settings-btn" title="Personal settings" aria-label="Personal settings" aria-expanded="false"><i data-lucide="settings"></i></button>
745
749
  </div>
746
750
  </div>
747
751
 
@@ -780,19 +784,31 @@ body { background: transparent; color: inherit; }
780
784
  </div>
781
785
  </div>
782
786
 
783
- <!-- === User Settings (modal dialog) === -->
787
+ <!-- === Unified settings shell (full screen): Personal, Project and Server panes mount into it === -->
788
+ <div id="settings-shell" class="hidden" role="region" aria-label="Settings">
789
+ <header class="settings-shell-header">
790
+ <button type="button" id="settings-shell-close" class="settings-shell-close" aria-label="Close settings"><i data-lucide="arrow-left" aria-hidden="true"></i><span>Back</span><kbd>Esc</kbd></button>
791
+ <div class="settings-scope-tabs" id="settings-scope-tabs" role="group" aria-label="Settings scope"></div>
792
+ </header>
793
+ <div class="settings-shell-body">
794
+ <nav class="settings-shell-nav" aria-label="Settings navigation">
795
+ <div class="settings-shell-nav-inner">
796
+ <div class="settings-scope-head"><strong id="settings-scope-title">Settings</strong><small id="settings-server-name" hidden></small><span id="settings-scope-hint" class="settings-scope-hint"></span></div>
797
+ <div class="settings-project-picker" id="settings-project-picker" hidden><label class="settings-label" for="settings-project-select">Project</label><select id="settings-project-select" class="ps-select" aria-label="Project"></select></div>
798
+ <select id="settings-page-select" class="settings-nav-dropdown" aria-label="Settings page"></select>
799
+ <div class="settings-shell-nav-items" id="settings-page-nav"></div>
800
+ </div>
801
+ </nav>
802
+ <main class="settings-shell-content" id="settings-shell-content">
803
+ <p id="settings-shell-status" class="settings-shell-status" role="status" hidden></p>
804
+ </main>
805
+ </div>
806
+ </div>
807
+
808
+ <!-- === Personal settings pane === -->
784
809
  <div id="user-settings" class="hidden">
785
- <div class="confirm-backdrop" id="user-settings-backdrop"></div>
786
810
  <div class="us-modal">
787
- <div class="us-modal-header">
788
- <span class="us-modal-title">User Settings</span>
789
- <button class="us-modal-close" id="user-settings-close" title="Close"><i data-lucide="x"></i></button>
790
- </div>
791
811
  <div class="us-modal-body">
792
- <nav class="us-modal-nav">
793
- <select id="user-settings-nav-dropdown" class="settings-nav-dropdown"></select>
794
- <div class="us-modal-nav-items"></div>
795
- </nav>
796
812
  <div class="us-modal-content">
797
813
 
798
814
  <div class="us-section" data-section="us-notifications">
@@ -1031,16 +1047,8 @@ body { background: transparent; color: inherit; }
1031
1047
  </div>
1032
1048
 
1033
1049
  <!-- === Server Settings (full-screen overlay) === -->
1034
- <div id="server-settings" class="hidden" role="dialog" aria-modal="true" aria-label="Server settings">
1050
+ <div id="server-settings" class="hidden">
1035
1051
  <div class="server-settings-layout">
1036
- <nav class="server-settings-nav" aria-label="Server settings pages">
1037
- <div class="server-settings-nav-inner">
1038
- <div class="server-settings-nav-header">Server settings<small id="settings-server-name"></small></div>
1039
- <select id="settings-nav-dropdown" class="settings-nav-dropdown" aria-label="Settings page"></select>
1040
- <div class="server-settings-nav-items" id="settings-page-nav"></div>
1041
- <button class="settings-nav-item" id="settings-personal-link">Personal notifications ↗</button>
1042
- </div>
1043
- </nav>
1044
1052
  <div class="server-settings-content"><div class="server-settings-content-inner">
1045
1053
  <p id="settings-access-status" role="status">Checking administrator access…</p>
1046
1054
  <div class="server-settings-section active" data-section="overview">
@@ -1248,7 +1256,7 @@ body { background: transparent; color: inherit; }
1248
1256
  <div class="settings-hint settings-restart-error hidden" id="settings-restart-error" style="margin-top: 8px;"></div>
1249
1257
  </div>
1250
1258
  </div>
1251
- </div><section class="settings-operation settings-danger-operation"><div class="settings-danger-entry"><div class="settings-danger-entry-copy"><h3>Danger zone</h3><p>Shut down Clay for everyone. Active work stops; restarting requires access to the host.</p></div><button type="button" class="settings-btn-sm settings-btn-danger" id="settings-shutdown-review" aria-expanded="false" aria-controls="settings-shutdown-flow"><i data-lucide="power" aria-hidden="true"></i>Review shutdown…</button></div><div id="settings-shutdown-flow" hidden role="group" aria-labelledby="settings-shutdown-title"><h4 id="settings-shutdown-title">Shut down server</h4><p class="settings-hint">Stops Clay, interrupts active work and disconnects everyone. You will need shell or service-manager access to start it again.</p><label class="settings-label" for="settings-shutdown-input">Type shutdown to continue</label><div class="settings-shutdown-input-row"><input type="text" id="settings-shutdown-input" autocomplete="off" spellcheck="false"><button class="settings-btn-sm settings-btn-danger" id="settings-shutdown-btn" disabled>Shut down…</button></div><p id="settings-shutdown-error" class="settings-hint hidden" role="status"></p><button type="button" class="settings-btn-sm" id="settings-shutdown-back">Back</button></div></section></div></div></div><div class="server-settings-close-col"><button id="server-settings-close" class="server-settings-close-btn" aria-label="Close server settings"><i data-lucide="x"></i><span>ESC</span></button></div></div></div>
1259
+ </div><section class="settings-operation settings-danger-operation"><div class="settings-danger-entry"><div class="settings-danger-entry-copy"><h3>Danger zone</h3><p>Shut down Clay for everyone. Active work stops; restarting requires access to the host.</p></div><button type="button" class="settings-btn-sm settings-btn-danger" id="settings-shutdown-review" aria-expanded="false" aria-controls="settings-shutdown-flow"><i data-lucide="power" aria-hidden="true"></i>Review shutdown…</button></div><div id="settings-shutdown-flow" hidden role="group" aria-labelledby="settings-shutdown-title"><h4 id="settings-shutdown-title">Shut down server</h4><p class="settings-hint">Stops Clay, interrupts active work and disconnects everyone. You will need shell or service-manager access to start it again.</p><label class="settings-label" for="settings-shutdown-input">Type shutdown to continue</label><div class="settings-shutdown-input-row"><input type="text" id="settings-shutdown-input" autocomplete="off" spellcheck="false"><button class="settings-btn-sm settings-btn-danger" id="settings-shutdown-btn" disabled>Shut down…</button></div><p id="settings-shutdown-error" class="settings-hint hidden" role="status"></p><button type="button" class="settings-btn-sm" id="settings-shutdown-back">Back</button></div></section></div></div></div></div></div>
1252
1260
 
1253
1261
  <div id="paste-modal" class="hidden" role="dialog" aria-modal="true" aria-labelledby="paste-modal-title">
1254
1262
  <div class="confirm-backdrop"></div>
@@ -1358,37 +1366,16 @@ body { background: transparent; color: inherit; }
1358
1366
  </div>
1359
1367
  </div>
1360
1368
 
1361
- <div id="add-project-modal" class="hidden">
1362
- <div class="confirm-backdrop"></div>
1363
- <div class="confirm-dialog add-project-dialog">
1364
- <div class="add-project-title">Add project</div>
1365
- <div id="add-project-removed" class="hidden"></div>
1366
- <div class="add-project-modes">
1367
- <button class="add-project-mode-btn active" data-mode="existing">Existing directory</button>
1368
- <button class="add-project-mode-btn" data-mode="create">New project</button>
1369
- <button class="add-project-mode-btn" data-mode="clone">Clone from GitHub</button>
1370
- </div>
1371
- <div class="add-project-body">
1372
- <div class="add-project-panel active" data-panel="existing">
1373
- <div class="add-project-input-wrap">
1374
- <span id="add-project-prefix" class="add-project-prefix hidden"></span>
1375
- <input type="text" id="add-project-input" placeholder="/" autocomplete="off" spellcheck="false">
1376
- <div id="add-project-suggestions" class="hidden"></div>
1377
- </div>
1378
- </div>
1379
- <div class="add-project-panel" data-panel="create">
1380
- <input type="text" id="add-project-create-input" placeholder="Project name" autocomplete="off" spellcheck="false">
1381
- </div>
1382
- <div class="add-project-panel" data-panel="clone">
1383
- <input type="text" id="add-project-clone-input" placeholder="https://github.com/user/repo" autocomplete="off" spellcheck="false">
1384
- <div id="add-project-clone-progress" class="hidden"><span class="clone-spinner"></span><span id="add-project-clone-text">Cloning...</span></div>
1385
- </div>
1386
- <div id="add-project-error" class="hidden"></div>
1387
- </div>
1388
- <div class="confirm-actions">
1389
- <button class="confirm-btn confirm-cancel" id="add-project-cancel">Cancel</button>
1390
- <button class="confirm-btn confirm-ok" id="add-project-ok">Add</button>
1391
- </div>
1369
+ <div id="project-start-modal" class="hidden">
1370
+ <div class="project-start-backdrop"></div>
1371
+ <div class="project-start-dialog" role="dialog" aria-modal="true" aria-labelledby="project-start-title" aria-describedby="project-start-subtitle" tabindex="-1">
1372
+ <header class="project-start-header">
1373
+ <button type="button" class="project-start-icon-btn" id="project-start-back" aria-label="Back to choices" hidden><i data-lucide="arrow-left"></i></button>
1374
+ <h2 id="project-start-title">Start a project</h2>
1375
+ <button type="button" class="project-start-icon-btn" id="project-start-close" aria-label="Close"><i data-lucide="x"></i></button>
1376
+ </header>
1377
+ <p class="project-start-subtitle" id="project-start-subtitle">Start something new, or pick up where you left off.</p>
1378
+ <div class="project-start-body" id="project-start-body"></div>
1392
1379
  </div>
1393
1380
  </div>
1394
1381
 
@@ -1,7 +1,7 @@
1
1
  import { store } from './store.js';
2
2
  import { iconHtml, refreshIcons } from './icons.js';
3
3
  import { showToast, copyToClipboard, escapeHtml } from './utils.js';
4
- import { apiGet, apiPost, apiPut, apiDelete, showConfirmDialog, showConfirmResetPin, prepareAdminModal } from './admin-settings-ui.js';
4
+ import { apiGet, apiPost, apiPut, apiDelete, showConfirmDialog, showConfirmResetPin, prepareAdminModal, mountAdminSubview } from './admin-settings-ui.js';
5
5
  // --- Invites ---
6
6
  export function loadInvitesTab(body) {
7
7
  apiGet("/api/admin/invites").then(function (data) {
@@ -138,7 +138,7 @@ function showEmailInvitePrompt(body) {
138
138
  '<button class="admin-modal-cancel">Cancel</button>' +
139
139
  '</div></div>';
140
140
  modal.innerHTML = html;
141
- document.body.appendChild(modal);
141
+ mountAdminSubview(modal);
142
142
  prepareAdminModal(modal);
143
143
  refreshIcons(modal);
144
144
 
@@ -1,7 +1,7 @@
1
1
  import { store } from './store.js';
2
2
  import { iconHtml, refreshIcons } from './icons.js';
3
3
  import { showToast, copyToClipboard, escapeHtml } from './utils.js';
4
- import { apiGet, apiPost, apiPut, apiDelete, showConfirmDialog, showConfirmResetPin, prepareAdminModal } from './admin-settings-ui.js';
4
+ import { apiGet, apiPost, apiPut, apiDelete, showConfirmDialog, showConfirmResetPin, prepareAdminModal, mountAdminSubview } from './admin-settings-ui.js';
5
5
  // --- Projects ---
6
6
  export function loadProjectsTab(body) {
7
7
  var projectList = store.get('projectsHubList') || [];
@@ -168,7 +168,7 @@ function showProjectUsersModal(slug, parentBody) {
168
168
  html += '</div>';
169
169
 
170
170
  modal.innerHTML = html;
171
- document.body.appendChild(modal);
171
+ mountAdminSubview(modal);
172
172
  prepareAdminModal(modal);
173
173
  refreshIcons(modal);
174
174
 
@@ -14,13 +14,29 @@ export function apiPost(url, body) { return request(url, 'POST', body || {}); }
14
14
  export function apiPut(url, body) { return request(url, 'PUT', body); }
15
15
  export function apiDelete(url) { return request(url, 'DELETE'); }
16
16
 
17
+ // Editors open inline in the active Server settings page (a subview with its
18
+ // own Back/Cancel path) instead of over the page. Only destructive
19
+ // confirmations remain overlay dialogs.
20
+ export function mountAdminSubview(modal) {
21
+ var host = document.querySelector('#server-settings .server-settings-section.active');
22
+ if (!host) { document.body.appendChild(modal); return; }
23
+ modal.className = 'admin-subview settings-subview';
24
+ host.classList.add('has-subview');
25
+ host.appendChild(modal);
26
+ var observer = new MutationObserver(function () {
27
+ if (!host.querySelector('.settings-subview')) { host.classList.remove('has-subview'); observer.disconnect(); }
28
+ });
29
+ observer.observe(host, { childList: true });
30
+ }
31
+
17
32
  export function prepareAdminModal(modal) {
18
33
  if (modal.dataset.prepared) return;
19
34
  modal.dataset.prepared = 'true';
20
35
  var previous = document.activeElement;
21
- var restore = isolateSettingsDialog(modal);
36
+ var inline = modal.classList.contains('settings-subview');
37
+ var restore = inline ? function () {} : isolateSettingsDialog(modal);
22
38
  var dialog = modal.querySelector('.admin-modal');
23
- dialog.setAttribute('role', 'dialog'); dialog.setAttribute('aria-modal', 'true');
39
+ dialog.setAttribute('role', inline ? 'region' : 'dialog'); if (!inline) dialog.setAttribute('aria-modal', 'true');
24
40
  var title = dialog.querySelector('h3'); dialog.setAttribute('aria-label', title ? title.textContent : 'Confirm action');
25
41
  var dirty = false;
26
42
  modal.addEventListener('input', function () { dirty = true; });
@@ -37,7 +53,7 @@ export function prepareAdminModal(modal) {
37
53
  modal.addEventListener('keydown', function (event) {
38
54
  event.stopPropagation();
39
55
  if (event.key === 'Escape') { event.preventDefault(); if (dirty) cancel(event); else modal.remove(); }
40
- trapSettingsFocus(event, modal);
56
+ if (!inline) trapSettingsFocus(event, modal);
41
57
  });
42
58
  modal.querySelectorAll('.admin-smtp-row').forEach(function (row) {
43
59
  var label = row.querySelector('label'); var input = row.querySelector('input, select');
@@ -49,7 +65,7 @@ export function prepareAdminModal(modal) {
49
65
  var closeObserver = new MutationObserver(function () {
50
66
  if (!modal.isConnected) { closeObserver.disconnect(); restore(); if (previous && previous.isConnected) previous.focus(); }
51
67
  });
52
- closeObserver.observe(document.body, { childList: true });
68
+ closeObserver.observe(inline ? modal.parentElement : document.body, { childList: true });
53
69
  var focus = modal.querySelector('input:not([type=hidden]), .admin-modal-cancel, button');
54
70
  if (focus) focus.focus();
55
71
  }
@@ -1,7 +1,7 @@
1
1
  import { store } from './store.js';
2
2
  import { iconHtml, refreshIcons } from './icons.js';
3
3
  import { showToast, copyToClipboard, escapeHtml } from './utils.js';
4
- import { apiGet, apiPost, apiPut, apiDelete, showConfirmDialog, showConfirmResetPin, prepareAdminModal } from './admin-settings-ui.js';
4
+ import { apiGet, apiPost, apiPut, apiDelete, showConfirmDialog, showConfirmResetPin, prepareAdminModal, mountAdminSubview } from './admin-settings-ui.js';
5
5
  // --- Users ---
6
6
  export function loadUsersTab(body) {
7
7
  apiGet("/api/admin/users").then(function (data) {
@@ -163,7 +163,7 @@ function showPermissionsModal(user, body) {
163
163
  '</div></div>';
164
164
 
165
165
  modal.innerHTML = html;
166
- document.body.appendChild(modal);
166
+ mountAdminSubview(modal);
167
167
  prepareAdminModal(modal);
168
168
  refreshIcons(modal);
169
169
 
@@ -222,7 +222,7 @@ function showAddUserModal(body) {
222
222
  '<button class="admin-modal-cancel">Cancel</button>' +
223
223
  '</div></div>';
224
224
  modal.innerHTML = html;
225
- document.body.appendChild(modal);
225
+ mountAdminSubview(modal);
226
226
  prepareAdminModal(modal);
227
227
  refreshIcons(modal);
228
228
 
@@ -299,7 +299,7 @@ function showTempPinModal(user, tempPin) {
299
299
  '<button class="admin-modal-cancel">Close</button>' +
300
300
  '</div></div>';
301
301
  modal.innerHTML = html;
302
- document.body.appendChild(modal);
302
+ mountAdminSubview(modal);
303
303
  prepareAdminModal(modal);
304
304
  refreshIcons(modal);
305
305
 
@@ -3,7 +3,8 @@
3
3
  import { showProjectsHub, hideProjectsHub } from './projects-hub.js';
4
4
  import { store } from './store.js';
5
5
  import { getWs } from './ws-ref.js';
6
- import { getCachedProjects, openAddProjectModal, switchProject } from './app-projects.js';
6
+ import { getCachedProjects, switchProject } from './app-projects.js';
7
+ import { openAddProjectModal } from './project-start-dialog.js';
7
8
  import { chooseProjectActivationTarget, homeProjectCreationState } from './project-activation.js';
8
9
  import { mateAvatarUrl } from './avatar.js';
9
10
  import { exitDmMode } from './app-dm.js';
@@ -65,7 +65,7 @@ import { showDoneNotification, playDoneSound, isNotifAlertEnabled, isNotifSoundE
65
65
  import { handleFsList, handleFsRead, handleFileChanged, handleDirChanged, handleFileHistory, handleGitDiff, handleFileAt, refreshIfOpen, getPendingNavigate, handleFsSearch, presentMarkdownEdit } from './filebrowser.js';
66
66
  import { beginMarkdownTurn, finishMarkdownTurn, markdownPathFromToolInput } from './markdown-live-edit.js';
67
67
  import { forwardPaneMarkdownPresentation } from './pane-bridge.js';
68
- import { handleProjectOwnerChanged } from './project-settings.js';
68
+ import { handleProjectOwnerChanged, handleProjectSettingsEditResult } from './project-settings.js';
69
69
  import { handleProjectEnv, handleProjectEnvSaved } from './project-settings-environment.js';
70
70
  import { handleProjectSessionVisibilityDefault, refreshProjectSessionVisibilitySettings } from './project-session-visibility-settings.js';
71
71
  import { updateSettingsStats, updateDaemonConfig, handleSetPinResult, handleKeepAwakeChanged, handleAutoContinueChanged, handleRestartResult, handleShutdownResult, handleSharedEnv, handleSharedEnvSaved, handleServerSettingResult } from './server-settings.js';
@@ -100,13 +100,15 @@ import { markProjectSessionListHydrated, applyProjectSplitGroups, clearProjectSp
100
100
  import { selectDefaultVendorForBlankSession } from './vendor-selection.js';
101
101
  import { getModelInfoUpdate, modelEntryValue, modelEntryMatches, handleModelSelectionResult, requestVendorModels } from './model-picker.js';
102
102
  import { getModelEffortLevels, accumulateUsage, updateUsagePanel, accumulateContext, updateContextPanel, renderCtxPopover, updateStatusPanel, markContextUnavailable } from './app-panels.js';
103
- import { updateProjectList, resetClientState, showUpdateAvailable, handleRemoveProjectCheckResult, handleRemoveProjectResult, handleBrowseDirResult, handleAddProjectResult, handleCloneProgress, finishProjectSessionActivation } from './app-projects.js';
103
+ import { updateProjectList, resetClientState, showUpdateAvailable, handleRemoveProjectCheckResult, handleRemoveProjectResult, finishProjectSessionActivation } from './app-projects.js';
104
+ import { handleBrowseDirResult, handleAddProjectResult, handleCloneProgress } from './project-start-dialog.js';
104
105
  import { updateHistorySentinel, prependOlderHistory } from './app-header.js';
105
106
  import { handleToolsState, handleToolInstalled, handleToolRemoved, handleToolStorageResult, handleToolLlmResult, handleToolsError, handleToolControlRequest, handleToolServerState, handleToolServerEvent, handleCapsuleGameSession } from './home-tools.js';
106
107
  import { handleHomeDockState } from './home-dock.js';
107
108
  import { handleToolFrameUrlState } from './home-tool-frame.js';
108
109
  import { enterDmMode, exitDmMode, handleMateCreatedInApp, updateMateIconStatus, appendDmMessage, showDmTypingIndicator, buildMateInterviewPrompt } from './app-dm.js';
109
110
  import { handleRateLimitEvent, updateRateLimitUsage, handleFastModeState } from './app-rate-limit.js';
111
+ import { handleVendorUsageOverview } from './vendor-usage-overview.js';
110
112
  import { handleRemoteCursorMove, handleRemoteCursorLeave, handleRemoteSelection, clearRemoteCursors } from './app-cursors.js';
111
113
  import { showLoopBanner, updateLoopBanner, updateLoopInputVisibility, showRalphApprovalBar, updateRalphApprovalStatus, openRalphPreviewModal, showExecModal, updateExecModalStatus } from './app-loop-ui.js';
112
114
  import { handleAutonomousRunMessage, syncAutonomousRunForSession } from './autonomous-run.js';
@@ -1615,6 +1617,10 @@ export function processMessage(msg) {
1615
1617
  updateRateLimitUsage(msg);
1616
1618
  break;
1617
1619
 
1620
+ case "vendor_usage_overview":
1621
+ handleVendorUsageOverview(msg);
1622
+ break;
1623
+
1618
1624
  case "auto_continue_scheduled":
1619
1625
  // Scheduler auto-continue, just show info
1620
1626
  break;
@@ -1922,12 +1928,14 @@ export function processMessage(msg) {
1922
1928
  break;
1923
1929
 
1924
1930
  case "set_project_title_result":
1931
+ handleProjectSettingsEditResult(msg);
1925
1932
  if (!msg.ok) {
1926
1933
  showToast(msg.error || "Failed to rename project", "error");
1927
1934
  }
1928
1935
  break;
1929
1936
 
1930
1937
  case "set_project_icon_result":
1938
+ handleProjectSettingsEditResult(msg);
1931
1939
  if (!msg.ok) {
1932
1940
  showToast(msg.error || "Failed to set icon", "error");
1933
1941
  }