clay-server 4.1.0 → 4.2.0-beta.1
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/lib/daemon-project-access.js +14 -0
- package/lib/daemon.js +7 -0
- package/lib/project-issue-launch.js +3 -1
- package/lib/project-sessions.js +32 -6
- package/lib/project.js +4 -0
- package/lib/public/app.js +1 -0
- package/lib/public/css/command-palette.css +51 -33
- package/lib/public/css/issues.css +4 -4
- package/lib/public/index.html +10 -0
- package/lib/public/modules/app-messages.js +7 -0
- package/lib/public/modules/clay-issue-links.js +1 -0
- package/lib/public/modules/clay-log-links.js +1 -0
- package/lib/public/modules/project-session-visibility-settings.js +138 -0
- package/lib/public/modules/project-settings.js +5 -0
- package/lib/public/modules/sidebar-projects.js +14 -3
- package/lib/server.js +8 -1
- package/lib/session-visibility.js +62 -1
- package/lib/sessions.js +2 -0
- package/lib/ws-schema.js +2 -0
- package/package.json +1 -1
|
@@ -41,6 +41,7 @@ function getProjectAccess(config, slug, osUsers) {
|
|
|
41
41
|
visibility: visibility,
|
|
42
42
|
allowedUsers: parentAllowedUsers,
|
|
43
43
|
ownerId: project.ownerId || null,
|
|
44
|
+
sessionVisibilityDefault: project.sessionVisibilityDefault === "shared" ? "shared" : "private",
|
|
44
45
|
isWorktree: false,
|
|
45
46
|
};
|
|
46
47
|
}
|
|
@@ -52,6 +53,7 @@ function getProjectAccess(config, slug, osUsers) {
|
|
|
52
53
|
visibility: visibility,
|
|
53
54
|
allowedUsers: uniqueUsers(parentAllowedUsers.concat(worktreeAllowedUsers)),
|
|
54
55
|
ownerId: project.ownerId || null,
|
|
56
|
+
sessionVisibilityDefault: project.sessionVisibilityDefault === "shared" ? "shared" : "private",
|
|
55
57
|
isWorktree: true,
|
|
56
58
|
parentSlug: parentSlug,
|
|
57
59
|
parentAllowedUsers: parentAllowedUsers,
|
|
@@ -61,6 +63,7 @@ function getProjectAccess(config, slug, osUsers) {
|
|
|
61
63
|
visibility: visibility,
|
|
62
64
|
allowedUsers: parentAllowedUsers,
|
|
63
65
|
ownerId: project.ownerId || null,
|
|
66
|
+
sessionVisibilityDefault: project.sessionVisibilityDefault === "shared" ? "shared" : "private",
|
|
64
67
|
},
|
|
65
68
|
};
|
|
66
69
|
}
|
|
@@ -84,6 +87,16 @@ function setAllowedUsers(config, slug, allowedUsers) {
|
|
|
84
87
|
return { ok: true, isWorktree: true, allowedUsers: clean };
|
|
85
88
|
}
|
|
86
89
|
|
|
90
|
+
function setSessionVisibilityDefault(config, slug, visibility) {
|
|
91
|
+
if (visibility !== "private" && visibility !== "shared") return { error: "Invalid session visibility default" };
|
|
92
|
+
if (parentSlugFor(slug)) return { error: "Worktrees inherit this setting from their parent project." };
|
|
93
|
+
var project = findProject(config, slug);
|
|
94
|
+
if (!project) return { error: "Project not found" };
|
|
95
|
+
if (visibility === "shared") project.sessionVisibilityDefault = "shared";
|
|
96
|
+
else delete project.sessionVisibilityDefault;
|
|
97
|
+
return { ok: true, visibility: visibility };
|
|
98
|
+
}
|
|
99
|
+
|
|
87
100
|
function clearWorktreeGrant(config, slug) {
|
|
88
101
|
var parentSlug = parentSlugFor(slug);
|
|
89
102
|
var project = parentSlug ? findProject(config, parentSlug) : null;
|
|
@@ -113,6 +126,7 @@ module.exports = {
|
|
|
113
126
|
parentSlugFor: parentSlugFor,
|
|
114
127
|
getProjectAccess: getProjectAccess,
|
|
115
128
|
setAllowedUsers: setAllowedUsers,
|
|
129
|
+
setSessionVisibilityDefault: setSessionVisibilityDefault,
|
|
116
130
|
clearWorktreeGrant: clearWorktreeGrant,
|
|
117
131
|
reconcileWorktreeGrants: reconcileWorktreeGrants,
|
|
118
132
|
};
|
package/lib/daemon.js
CHANGED
|
@@ -556,6 +556,13 @@ var relay = createServer({
|
|
|
556
556
|
});
|
|
557
557
|
return { ok: true };
|
|
558
558
|
},
|
|
559
|
+
onSetProjectSessionVisibilityDefault: function (slug, visibility) {
|
|
560
|
+
var result = daemonProjectAccess.setSessionVisibilityDefault(config, slug, visibility);
|
|
561
|
+
if (result.error) return { ok: false, error: result.error };
|
|
562
|
+
saveConfig(config);
|
|
563
|
+
relay.broadcastAll({ type: "projects_updated", projects: relay.getProjects(), projectCount: config.projects.length });
|
|
564
|
+
return result;
|
|
565
|
+
},
|
|
559
566
|
onProjectOwnerChanged: function (slug, ownerId) {
|
|
560
567
|
console.log("[daemon] onProjectOwnerChanged:", slug, "→", ownerId);
|
|
561
568
|
var oldOwnerId = null;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var yoke = require("./yoke");
|
|
4
4
|
var provenance = require("./session-provenance");
|
|
5
|
+
var sessionVisibility = require("./session-visibility");
|
|
5
6
|
|
|
6
7
|
function attachIssueLaunch(ctx) {
|
|
7
8
|
var requests = new Map();
|
|
@@ -79,8 +80,9 @@ function attachIssueLaunch(ctx) {
|
|
|
79
80
|
var liveLinuxUser = typeof ctx.getLinuxUserForSession === "function" ? ctx.getLinuxUserForSession({ ownerId: actorId }) : null;
|
|
80
81
|
if (osUsersEnabled() && liveLinuxUser !== expectedLinuxUser) throw new Error("The OS identity changed while issue work was being prepared.");
|
|
81
82
|
|
|
83
|
+
var projectAccess = typeof ctx.getProjectAccess === "function" ? ctx.getProjectAccess() : null;
|
|
82
84
|
var session = ctx.sm.createSessionRaw({ vendor: runtime.vendor, model: runtime.model, effort: runtime.effort || null,
|
|
83
|
-
mode: "gui", ownerId: actor && actor.id || null, sessionVisibility:
|
|
85
|
+
mode: "gui", ownerId: actor && actor.id || null, sessionVisibility: sessionVisibility.defaultForProject(projectAccess) });
|
|
84
86
|
provenance.ensureOrigin(session);
|
|
85
87
|
if (session.hidden === true || session.delegated === true || provenance.isWorker(session)) {
|
|
86
88
|
ctx.sm.deleteSession(session.localId);
|
package/lib/project-sessions.js
CHANGED
|
@@ -555,7 +555,7 @@ function attachSessions(ctx) {
|
|
|
555
555
|
}
|
|
556
556
|
sm.sweepBlankSessions();
|
|
557
557
|
var sessionOpts = {
|
|
558
|
-
sessionVisibility:
|
|
558
|
+
sessionVisibility: "private",
|
|
559
559
|
vendor: prepared.runtime.vendor,
|
|
560
560
|
model: prepared.runtime.model,
|
|
561
561
|
effort: prepared.runtime.effort || null,
|
|
@@ -619,7 +619,15 @@ function attachSessions(ctx) {
|
|
|
619
619
|
sm.sweepBlankSessions();
|
|
620
620
|
var sessionOpts = {};
|
|
621
621
|
if (ws._clayUser && usersModule.isMultiUser()) sessionOpts.ownerId = ws._clayUser.id;
|
|
622
|
-
|
|
622
|
+
// Only ordinary project conversations may opt into this setting.
|
|
623
|
+
// Scheduled interviews and background/worker creation paths remain explicit.
|
|
624
|
+
var visibilityChoice = sessionVisibility.resolveNewSessionVisibility(getProjectAccess(), msg, isMate);
|
|
625
|
+
if (visibilityChoice.error) {
|
|
626
|
+
scheduledRequestError(ws, msg, visibilityChoice.error);
|
|
627
|
+
if (!msg.requestId) sendTo(ws, { type: "error", text: visibilityChoice.error });
|
|
628
|
+
return true;
|
|
629
|
+
}
|
|
630
|
+
sessionOpts.sessionVisibility = visibilityChoice.visibility;
|
|
623
631
|
if (msg.vendor) sessionOpts.vendor = msg.vendor;
|
|
624
632
|
// Mode resolution: vendors without a TUI session mode are always GUI.
|
|
625
633
|
// TUI-capable sessions honor the explicit msg.mode if provided, otherwise
|
|
@@ -701,8 +709,7 @@ function attachSessions(ctx) {
|
|
|
701
709
|
if (reusable) {
|
|
702
710
|
if (sessionOpts.vendor && reusable.vendor !== sessionOpts.vendor) reusable.vendor = sessionOpts.vendor;
|
|
703
711
|
if (!reusable.effort) reusable.effort = sessionOpts.effort;
|
|
704
|
-
|
|
705
|
-
sessionVisibility.revokeViewers(ctx, reusable);
|
|
712
|
+
// Reusable blanks already exist; do not retroactively change them.
|
|
706
713
|
sm.switchSession(reusable.localId, ws);
|
|
707
714
|
newSess = reusable;
|
|
708
715
|
} else {
|
|
@@ -735,6 +742,14 @@ function attachSessions(ctx) {
|
|
|
735
742
|
return true;
|
|
736
743
|
}
|
|
737
744
|
|
|
745
|
+
if (msg.type === "set_project_session_visibility_default") {
|
|
746
|
+
sessionVisibility.handleDefaultChange({
|
|
747
|
+
slug: slug, isMate: isMate, osUsers: osUsers, usersModule: usersModule, getProjectAccess: getProjectAccess,
|
|
748
|
+
opts: opts, sendTo: sendTo,
|
|
749
|
+
}, ws, msg);
|
|
750
|
+
return true;
|
|
751
|
+
}
|
|
752
|
+
|
|
738
753
|
if (msg.type === "set_session_bookmark") {
|
|
739
754
|
if (typeof msg.sessionId === "number") {
|
|
740
755
|
var bookmarkTarget = sm.sessions.get(msg.sessionId);
|
|
@@ -1493,7 +1508,11 @@ function attachSessions(ctx) {
|
|
|
1493
1508
|
} else {
|
|
1494
1509
|
forkHistory = session.history.slice();
|
|
1495
1510
|
}
|
|
1496
|
-
var forked = sm.createSession({
|
|
1511
|
+
var forked = sm.createSession({
|
|
1512
|
+
vendor: session.vendor,
|
|
1513
|
+
ownerId: session.ownerId || null,
|
|
1514
|
+
sessionVisibility: sessionVisibility.normalize(session.sessionVisibility),
|
|
1515
|
+
}, ws);
|
|
1497
1516
|
forked.cliSessionId = result.sessionId;
|
|
1498
1517
|
forked.title = forkTitle;
|
|
1499
1518
|
forked.history = forkHistory;
|
|
@@ -1511,7 +1530,14 @@ function attachSessions(ctx) {
|
|
|
1511
1530
|
// Read history from CLI session files
|
|
1512
1531
|
var cliSess = require("./cli-sessions");
|
|
1513
1532
|
return cliSess.readCliSessionHistory(resolveSessionHome(session), cwd, result.sessionId).then(function(history) {
|
|
1514
|
-
var forked = sm.resumeSession(result.sessionId, {
|
|
1533
|
+
var forked = sm.resumeSession(result.sessionId, {
|
|
1534
|
+
history: history,
|
|
1535
|
+
title: forkTitle,
|
|
1536
|
+
vendor: session.vendor,
|
|
1537
|
+
ownerId: session.ownerId || null,
|
|
1538
|
+
sessionVisibility: sessionVisibility.normalize(session.sessionVisibility),
|
|
1539
|
+
sessionVisibilityExplicit: session.sessionVisibilityExplicit === true,
|
|
1540
|
+
}, ws);
|
|
1515
1541
|
if (forked) {
|
|
1516
1542
|
ws._clayActiveSession = forked.localId;
|
|
1517
1543
|
sendTo(ws, { type: "fork_complete", sessionId: forked.localId });
|
package/lib/project.js
CHANGED
|
@@ -794,6 +794,10 @@ function createProjectContext(opts) {
|
|
|
794
794
|
var _issueWork = require("./project-issue-launch").attachIssueLaunch({
|
|
795
795
|
sm: sm, resolveDefaultAi: opts.resolveDefaultAi, getSdk: function () { return sdk; },
|
|
796
796
|
getLinuxUserForSession: getLinuxUserForSession, osUsers: osUsers,
|
|
797
|
+
getProjectAccess: function () {
|
|
798
|
+
if (isMate) return { visibility: "private", ownerId: projectOwnerId || null, allowedUsers: [] };
|
|
799
|
+
return opts.getProjectAccess ? opts.getProjectAccess() : { visibility: "public", ownerId: projectOwnerId || null };
|
|
800
|
+
},
|
|
797
801
|
ensureProjectAccessForSession: ensureProjectAccessForSession,
|
|
798
802
|
canStartWork: function (ws, actor) { return clients.has(ws) && ws.readyState === 1 && ws._clayUser === actor; },
|
|
799
803
|
onProcessingChanged: onProcessingChanged, sendTo: sendTo,
|
package/lib/public/app.js
CHANGED
|
@@ -432,6 +432,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
432
432
|
defaultAiDraft: { vendor: "", model: "", effort: "" },
|
|
433
433
|
defaultAiDraftDirty: false,
|
|
434
434
|
defaultVendorState: { loading: false, saving: false, getRequestId: null, saveRequestId: null, preference: null, preferencePresent: false, installedVendors: [], accountId: null, projectSlug: null, serverEpoch: null, canonicalRevision: 0, error: "" },
|
|
435
|
+
projectSessionVisibilityState: { active: null, pending: null },
|
|
435
436
|
|
|
436
437
|
// dm
|
|
437
438
|
dmTargetUser: null,
|
|
@@ -512,9 +512,45 @@ body.mate-dm-active .search-clay-transcript .search-clay-message-user .bubble,
|
|
|
512
512
|
.search-clay-activity-item small { color: var(--text-dimmer); font-size: 10px; line-height: 1.35; }
|
|
513
513
|
@keyframes searchClaySpin { to { transform: rotate(360deg); } }
|
|
514
514
|
|
|
515
|
-
.clayos-
|
|
516
|
-
|
|
517
|
-
|
|
515
|
+
.clayos-record-link {
|
|
516
|
+
display: inline-flex;
|
|
517
|
+
align-items: center;
|
|
518
|
+
gap: 6px;
|
|
519
|
+
min-height: 28px;
|
|
520
|
+
margin: 2px 1px;
|
|
521
|
+
padding: 3px 8px 3px 6px;
|
|
522
|
+
border: 1px solid color-mix(in srgb, var(--record-color, var(--accent)) 32%, var(--border));
|
|
523
|
+
border-radius: 8px;
|
|
524
|
+
background: color-mix(in srgb, var(--record-color, var(--accent)) 7%, var(--bg-alt));
|
|
525
|
+
color: var(--text);
|
|
526
|
+
font: inherit;
|
|
527
|
+
font-size: 11px;
|
|
528
|
+
line-height: 1;
|
|
529
|
+
vertical-align: middle;
|
|
530
|
+
cursor: pointer;
|
|
531
|
+
}
|
|
532
|
+
.clayos-record-link::before {
|
|
533
|
+
content: "\2197";
|
|
534
|
+
display: grid;
|
|
535
|
+
place-items: center;
|
|
536
|
+
width: 17px;
|
|
537
|
+
height: 17px;
|
|
538
|
+
border-radius: 5px;
|
|
539
|
+
background: color-mix(in srgb, var(--record-color, var(--accent)) 14%, transparent);
|
|
540
|
+
color: var(--record-color, var(--accent));
|
|
541
|
+
font-size: 12px;
|
|
542
|
+
}
|
|
543
|
+
.clayos-record-link > span { font-weight: 620; }
|
|
544
|
+
.clayos-record-link > small { min-width: 0; max-width: 28em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border-inline-start: 1px solid color-mix(in srgb, currentColor 22%, transparent); padding-inline-start: 6px; color: var(--text-muted); font-size: 11px; }
|
|
545
|
+
.clayos-record-link:hover { border-color: color-mix(in srgb, var(--record-color, var(--accent)) 58%, var(--border)); background: color-mix(in srgb, var(--record-color, var(--accent)) 11%, var(--bg-alt)); }
|
|
546
|
+
.clayos-record-link:focus-visible { outline: 2px solid var(--record-color, var(--accent)); outline-offset: 2px; }
|
|
547
|
+
.clayos-record-link.is-loading { opacity: 0.62; cursor: progress; }
|
|
548
|
+
.clayos-record-link.is-loading::before { animation: searchClaySpin 1.2s linear infinite; }
|
|
549
|
+
.clayos-record-link.is-error { border-color: color-mix(in srgb, var(--warning) 48%, var(--border)); }
|
|
550
|
+
.clayos-issue-link { --record-color: var(--link, var(--accent)); }
|
|
551
|
+
.clayos-log-link { --record-color: var(--accent); }
|
|
552
|
+
|
|
553
|
+
.clayos-session-link {
|
|
518
554
|
display: inline-flex;
|
|
519
555
|
align-items: center;
|
|
520
556
|
gap: 6px;
|
|
@@ -531,9 +567,7 @@ body.mate-dm-active .search-clay-transcript .search-clay-message-user .bubble,
|
|
|
531
567
|
vertical-align: middle;
|
|
532
568
|
cursor: pointer;
|
|
533
569
|
}
|
|
534
|
-
.clayos-session-link::before
|
|
535
|
-
.clayos-issue-link::before,
|
|
536
|
-
.clayos-log-link::before {
|
|
570
|
+
.clayos-session-link::before {
|
|
537
571
|
content: "\2197";
|
|
538
572
|
display: grid;
|
|
539
573
|
place-items: center;
|
|
@@ -544,29 +578,13 @@ body.mate-dm-active .search-clay-transcript .search-clay-message-user .bubble,
|
|
|
544
578
|
color: var(--accent);
|
|
545
579
|
font-size: 12px;
|
|
546
580
|
}
|
|
547
|
-
.clayos-
|
|
548
|
-
.clayos-
|
|
549
|
-
.clayos-session-link
|
|
550
|
-
.clayos-
|
|
551
|
-
.clayos-
|
|
552
|
-
.clayos-session-link
|
|
553
|
-
.clayos-
|
|
554
|
-
.clayos-log-link > small { color: var(--text-muted); font-size: 10px; }
|
|
555
|
-
.clayos-session-link:hover,
|
|
556
|
-
.clayos-issue-link:hover,
|
|
557
|
-
.clayos-log-link:hover { border-color: color-mix(in srgb, var(--accent) 52%, var(--border)); background: color-mix(in srgb, var(--accent) 11%, var(--bg-alt)); }
|
|
558
|
-
.clayos-session-link:focus-visible,
|
|
559
|
-
.clayos-issue-link:focus-visible,
|
|
560
|
-
.clayos-log-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
561
|
-
.clayos-session-link.is-loading,
|
|
562
|
-
.clayos-issue-link.is-loading,
|
|
563
|
-
.clayos-log-link.is-loading { opacity: 0.62; cursor: progress; }
|
|
564
|
-
.clayos-session-link.is-loading::before,
|
|
565
|
-
.clayos-issue-link.is-loading::before,
|
|
566
|
-
.clayos-log-link.is-loading::before { animation: searchClaySpin 1.2s linear infinite; }
|
|
567
|
-
.clayos-session-link.is-error,
|
|
568
|
-
.clayos-issue-link.is-error,
|
|
569
|
-
.clayos-log-link.is-error { border-color: color-mix(in srgb, var(--warning) 48%, var(--border)); }
|
|
581
|
+
.clayos-session-link > span { font-weight: 620; }
|
|
582
|
+
.clayos-session-link > small { color: var(--text-muted); font-size: 10px; }
|
|
583
|
+
.clayos-session-link:hover { border-color: color-mix(in srgb, var(--accent) 52%, var(--border)); background: color-mix(in srgb, var(--accent) 11%, var(--bg-alt)); }
|
|
584
|
+
.clayos-session-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
585
|
+
.clayos-session-link.is-loading { opacity: 0.62; cursor: progress; }
|
|
586
|
+
.clayos-session-link.is-loading::before { animation: searchClaySpin 1.2s linear infinite; }
|
|
587
|
+
.clayos-session-link.is-error { border-color: color-mix(in srgb, var(--warning) 48%, var(--border)); }
|
|
570
588
|
|
|
571
589
|
.search-clay-pending {
|
|
572
590
|
align-self: flex-start;
|
|
@@ -925,11 +943,11 @@ body.mate-dm-active .search-clay-transcript .search-clay-message-user .bubble,
|
|
|
925
943
|
}
|
|
926
944
|
|
|
927
945
|
.clayos-issue-link { max-width: min(100%, 30em); box-sizing: border-box; }
|
|
946
|
+
.clayos-issue-link::before,
|
|
947
|
+
.clayos-log-link::before { content: "\2261"; }
|
|
928
948
|
.clayos-issue-link::before { content: "\25CE"; flex: 0 0 17px; }
|
|
929
|
-
.clayos-issue-link[data-status="in_progress"]::before { color:
|
|
930
|
-
.clayos-issue-link[data-status="resolved"]::before { content: "\2713"; color: var(--
|
|
949
|
+
.clayos-issue-link[data-status="in_progress"]::before { color: var(--warning, var(--accent)); }
|
|
950
|
+
.clayos-issue-link[data-status="resolved"]::before { content: "\2713"; color: var(--success, var(--accent)); }
|
|
931
951
|
.clayos-issue-link[data-status="closed"]::before { content: "\00D7"; color: var(--text-dimmer); }
|
|
932
952
|
.clayos-issue-link > span { flex-shrink: 0; }
|
|
933
|
-
.clayos-issue-link > small { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
934
953
|
.clayos-log-link { max-width: 100%; box-sizing: border-box; }
|
|
935
|
-
.clayos-log-link > small { min-width: 0; overflow-wrap: anywhere; line-height: 1.35; }
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
.issue-row small { font-size: 10.5px; font-family: var(--font-mono); }
|
|
47
47
|
.issue-row-meta { display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px; margin-top: 8px; color: var(--text-dimmer); font: 10.5px/1.5 var(--font-mono); }
|
|
48
48
|
.issue-row-meta .issue-status { font-family: var(--font-sans); }
|
|
49
|
-
.issue-status { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; letter-spacing: .03em; }
|
|
49
|
+
.issue-status { display: inline-flex; align-items: center; gap: 6px; color: var(--text-secondary); font-size: 11px; letter-spacing: .03em; }
|
|
50
50
|
.issue-status::before { content: ''; width: 7px; height: 7px; border: 1px solid currentColor; border-radius: 50%; }
|
|
51
|
-
.issue-status[data-status="in_progress"] { color:
|
|
52
|
-
.issue-status[data-status="resolved"] { color:
|
|
53
|
-
.issue-status[data-status="closed"] { color: var(--text-muted); }
|
|
51
|
+
.issue-status[data-status="in_progress"]::before { color: var(--warning, var(--accent)); }
|
|
52
|
+
.issue-status[data-status="resolved"]::before { color: var(--success, var(--accent)); }
|
|
53
|
+
.issue-status[data-status="closed"]::before { color: var(--text-muted); }
|
|
54
54
|
.issue-detail { max-width: 760px; margin: 0 auto; padding: 32px 28px 52px; }
|
|
55
55
|
.issue-doc-chips { display: flex; align-items: center; flex-wrap: wrap; gap: 7px; margin-bottom: 10px; }
|
|
56
56
|
.issue-detail .issue-doc-title { margin: 0 0 10px; font-family: var(--font-display); font-size: clamp(24px, 2.8vw, 34px); letter-spacing: -.025em; line-height: 1.12; overflow-wrap: anywhere; }
|
package/lib/public/index.html
CHANGED
|
@@ -830,6 +830,16 @@
|
|
|
830
830
|
</div>
|
|
831
831
|
</div>
|
|
832
832
|
</div>
|
|
833
|
+
<div class="settings-field" id="ps-session-visibility-default-field" style="display:none">
|
|
834
|
+
<label class="settings-label" for="ps-session-visibility-default">New session visibility</label>
|
|
835
|
+
<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>
|
|
836
|
+
<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>
|
|
837
|
+
<div class="settings-hint hidden" id="ps-session-visibility-default-non-current-hint">Open this project to change this setting.</div>
|
|
838
|
+
<select class="ps-select" id="ps-session-visibility-default">
|
|
839
|
+
<option value="private">Private</option>
|
|
840
|
+
<option value="shared">Shared with project members</option>
|
|
841
|
+
</select>
|
|
842
|
+
</div>
|
|
833
843
|
</div>
|
|
834
844
|
</div>
|
|
835
845
|
<!-- Defaults -->
|
|
@@ -38,6 +38,7 @@ import { handleFsList, handleFsRead, handleFileChanged, handleDirChanged, handle
|
|
|
38
38
|
import { beginMarkdownTurn, finishMarkdownTurn, markdownPathFromToolInput } from './markdown-live-edit.js';
|
|
39
39
|
import { forwardPaneMarkdownPresentation } from './pane-bridge.js';
|
|
40
40
|
import { isProjectSettingsOpen, handleInstructionsRead, handleInstructionsWrite, handleProjectEnv, handleProjectEnvSaved, handleProjectSharedEnv, handleProjectSharedEnvSaved, handleProjectOwnerChanged } from './project-settings.js';
|
|
41
|
+
import { handleProjectSessionVisibilityDefault, refreshProjectSessionVisibilitySettings } from './project-session-visibility-settings.js';
|
|
41
42
|
import { updateSettingsStats, updateDaemonConfig, handleSetPinResult, handleKeepAwakeChanged, handleInheritGroupsChanged, handleAutoContinueChanged, handleRestartResult, handleShutdownResult, handleSharedEnv, handleSharedEnvSaved, handleGlobalClaudeMdRead, handleGlobalClaudeMdWrite } from './server-settings.js';
|
|
42
43
|
import { handleTermList, handleTermCreated, sendTerminalCommand, handleTermOutput, handleTermResized, handleTermExited, handleTermClosed } from './terminal.js';
|
|
43
44
|
import { attachTuiView, detachTuiView, setTuiSuspendedView, tuiHandleTermOutput, tuiHandleTermResized, tuiHandleTermExited, tuiHandleTermClosed } from './session-tui-view.js';
|
|
@@ -103,6 +104,10 @@ export function processMessage(msg) {
|
|
|
103
104
|
if (handleScheduledTaskMessage(msg)) return;
|
|
104
105
|
if (handleDefaultVendorMessage(msg)) return;
|
|
105
106
|
if (handleContextViewMessage(msg)) return;
|
|
107
|
+
if (msg && msg.type === "set_project_session_visibility_default_result") {
|
|
108
|
+
handleProjectSessionVisibilityDefault(msg);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
106
111
|
if (msg && msg.type === "schedule_message_result") {
|
|
107
112
|
handleScheduleMessageResult(msg);
|
|
108
113
|
return;
|
|
@@ -375,6 +380,7 @@ export function processMessage(msg) {
|
|
|
375
380
|
if (spBanner) spBanner.classList.remove("hidden");
|
|
376
381
|
}
|
|
377
382
|
updateProjectList(msg);
|
|
383
|
+
refreshProjectSessionVisibilitySettings(msg.projects);
|
|
378
384
|
break;
|
|
379
385
|
|
|
380
386
|
case "update_available":
|
|
@@ -1735,6 +1741,7 @@ export function processMessage(msg) {
|
|
|
1735
1741
|
|
|
1736
1742
|
case "projects_updated":
|
|
1737
1743
|
updateProjectList(msg);
|
|
1744
|
+
refreshProjectSessionVisibilitySettings(msg.projects);
|
|
1738
1745
|
renderUserStrip();
|
|
1739
1746
|
break;
|
|
1740
1747
|
|
|
@@ -31,6 +31,7 @@ function createIssueLink(ref, labelText) {
|
|
|
31
31
|
var button = document.createElement("button");
|
|
32
32
|
button.type = "button";
|
|
33
33
|
button.className = "clayos-issue-link";
|
|
34
|
+
button.classList.add("clayos-record-link");
|
|
34
35
|
button.contentEditable = "false";
|
|
35
36
|
button.dataset.issueRef = ref;
|
|
36
37
|
button.setAttribute("aria-label", "Open Project Issue" + (labelText ? " " + labelText : ""));
|
|
@@ -28,6 +28,7 @@ function createLogLink(ref, labelText) {
|
|
|
28
28
|
var button = document.createElement("button");
|
|
29
29
|
button.type = "button";
|
|
30
30
|
button.className = "clayos-log-link";
|
|
31
|
+
button.classList.add("clayos-record-link");
|
|
31
32
|
button.contentEditable = "false";
|
|
32
33
|
button.dataset.logRef = ref;
|
|
33
34
|
button.setAttribute("aria-label", "Open Project Log" + (labelText ? " " + labelText : ""));
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { getWs } from './ws-ref.js';
|
|
3
|
+
import { showToast } from './utils.js';
|
|
4
|
+
|
|
5
|
+
var requestSequence = 0;
|
|
6
|
+
var initialized = false;
|
|
7
|
+
|
|
8
|
+
function selectElement() {
|
|
9
|
+
return document.getElementById("ps-session-visibility-default");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function pendingState() {
|
|
13
|
+
return (store.get('projectSessionVisibilityState') || {}).pending || null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function activeState() {
|
|
17
|
+
return (store.get('projectSessionVisibilityState') || {}).active || null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function setVisibilityState(active, pending) {
|
|
21
|
+
store.set({ projectSessionVisibilityState: { active: active || null, pending: pending || null } });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function setSelectState(select, value, disabled) {
|
|
25
|
+
if (!select) return;
|
|
26
|
+
select.value = value;
|
|
27
|
+
select.disabled = !!disabled;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isCurrentProject(active) {
|
|
31
|
+
return !!(active && active.slug && active.slug === store.get('currentSlug'));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isReadOnly(active) {
|
|
35
|
+
return !active || !active.authorized || active.isWorktree || !isCurrentProject(active);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function renderActiveState() {
|
|
39
|
+
var active = activeState();
|
|
40
|
+
var pending = pendingState();
|
|
41
|
+
var select = selectElement();
|
|
42
|
+
var inheritedHint = document.getElementById("ps-session-visibility-default-inherited-hint");
|
|
43
|
+
var nonCurrentHint = document.getElementById("ps-session-visibility-default-non-current-hint");
|
|
44
|
+
if (inheritedHint) inheritedHint.classList.toggle("hidden", !(active && active.isWorktree));
|
|
45
|
+
if (nonCurrentHint) nonCurrentHint.classList.toggle("hidden", !(active && active.authorized && !active.isWorktree && !isCurrentProject(active)));
|
|
46
|
+
if (!select || !active) return;
|
|
47
|
+
setSelectState(select, active.effectiveValue, isReadOnly(active) || !!(pending && pending.slug === active.slug));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isAuthorized(project) {
|
|
51
|
+
var users = store.get('cachedAllUsers') || [];
|
|
52
|
+
var userId = store.get('myUserId');
|
|
53
|
+
var currentUser = null;
|
|
54
|
+
for (var i = 0; i < users.length; i++) {
|
|
55
|
+
if (users[i].id === userId) { currentUser = users[i]; break; }
|
|
56
|
+
}
|
|
57
|
+
return !!(store.get('isMultiUserMode') && project &&
|
|
58
|
+
((project.projectOwnerId && project.projectOwnerId === userId) || (currentUser && currentUser.role === "admin")) &&
|
|
59
|
+
(!store.get('permissions') || store.get('permissions').projectSettings !== false));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function initProjectSessionVisibilitySettings() {
|
|
63
|
+
var select = selectElement();
|
|
64
|
+
if (!select || initialized) return;
|
|
65
|
+
initialized = true;
|
|
66
|
+
select.addEventListener("change", function () {
|
|
67
|
+
var ws = getWs();
|
|
68
|
+
var active = activeState();
|
|
69
|
+
var slug = active && active.slug || "";
|
|
70
|
+
var prior = active && active.effectiveValue || "private";
|
|
71
|
+
if (!slug || !ws || ws.readyState !== 1 || pendingState() || isReadOnly(active)) {
|
|
72
|
+
setSelectState(select, prior, isReadOnly(active) || !!pendingState());
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
requestSequence += 1;
|
|
76
|
+
var requestId = "project-session-visibility-" + Date.now() + "-" + requestSequence;
|
|
77
|
+
setVisibilityState(active, { requestId: requestId, slug: slug, requested: select.value, previous: prior });
|
|
78
|
+
setSelectState(select, select.value, true);
|
|
79
|
+
ws.send(JSON.stringify({ type: "set_project_session_visibility_default", slug: slug, visibility: select.value, requestId: requestId }));
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
store.subscribe(function (state, previous) {
|
|
83
|
+
if (state.currentSlug !== previous.currentSlug) renderActiveState();
|
|
84
|
+
if (state.connected || previous.connected !== true) return;
|
|
85
|
+
var pending = pendingState();
|
|
86
|
+
if (!pending) return;
|
|
87
|
+
var current = selectElement();
|
|
88
|
+
var active = activeState();
|
|
89
|
+
if (current && active && active.slug === pending.slug) setSelectState(current, pending.previous, isReadOnly(active));
|
|
90
|
+
setVisibilityState(active, null);
|
|
91
|
+
renderActiveState();
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function populateProjectSessionVisibilitySettings(slug, project) {
|
|
96
|
+
var field = document.getElementById("ps-session-visibility-default-field");
|
|
97
|
+
var select = selectElement();
|
|
98
|
+
if (field) field.style.display = isAuthorized(project) ? "" : "none";
|
|
99
|
+
if (!select) return;
|
|
100
|
+
var value = project && project.sessionVisibilityDefault === "shared" ? "shared" : "private";
|
|
101
|
+
var active = { slug: slug || "", effectiveValue: value, isWorktree: !!(project && project.isWorktree), authorized: isAuthorized(project) };
|
|
102
|
+
var pending = pendingState();
|
|
103
|
+
setVisibilityState(active, pending);
|
|
104
|
+
renderActiveState();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function refreshProjectSessionVisibilitySettings(projects) {
|
|
108
|
+
var select = selectElement();
|
|
109
|
+
if (!select) return;
|
|
110
|
+
var active = activeState();
|
|
111
|
+
var slug = active && active.slug || "";
|
|
112
|
+
var source = Array.isArray(projects) ? projects : [];
|
|
113
|
+
for (var i = 0; i < source.length; i++) {
|
|
114
|
+
if (source[i] && source[i].slug === slug) {
|
|
115
|
+
populateProjectSessionVisibilitySettings(slug, source[i]);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function handleProjectSessionVisibilityDefault(msg) {
|
|
122
|
+
var pending = pendingState();
|
|
123
|
+
if (!pending || msg.requestId !== pending.requestId || msg.slug !== pending.slug) return;
|
|
124
|
+
var active = activeState();
|
|
125
|
+
setVisibilityState(active, null);
|
|
126
|
+
var select = selectElement();
|
|
127
|
+
if (!select || !active || active.slug !== msg.slug) return;
|
|
128
|
+
if (!msg.ok) {
|
|
129
|
+
active = Object.assign({}, active, { effectiveValue: pending.previous });
|
|
130
|
+
setVisibilityState(active, null);
|
|
131
|
+
setSelectState(select, pending.previous, isReadOnly(active));
|
|
132
|
+
showToast(msg.error || "Failed to update new session visibility", "error");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
active = Object.assign({}, active, { effectiveValue: msg.visibility === "shared" ? "shared" : "private" });
|
|
136
|
+
setVisibilityState(active, null);
|
|
137
|
+
setSelectState(select, active.effectiveValue, isReadOnly(active));
|
|
138
|
+
}
|
|
@@ -4,6 +4,7 @@ import { showToast } from './utils.js';
|
|
|
4
4
|
import { parseEmojis } from './markdown.js';
|
|
5
5
|
import { closeFileViewer } from './filebrowser.js';
|
|
6
6
|
import { renderModelList, renderModeList, renderEffortBar, renderThinkingBar, renderBetaCard } from './settings-defaults.js';
|
|
7
|
+
import { initProjectSessionVisibilitySettings, populateProjectSessionVisibilitySettings } from './project-session-visibility-settings.js';
|
|
7
8
|
|
|
8
9
|
var ctx = null;
|
|
9
10
|
var panelEl = null;
|
|
@@ -162,6 +163,8 @@ export function initProjectSettings(appCtx, emojiCategories) {
|
|
|
162
163
|
hideTransferForm();
|
|
163
164
|
});
|
|
164
165
|
}
|
|
166
|
+
|
|
167
|
+
initProjectSessionVisibilitySettings();
|
|
165
168
|
}
|
|
166
169
|
|
|
167
170
|
// ===== Open / Close =====
|
|
@@ -246,6 +249,8 @@ function populateProfile() {
|
|
|
246
249
|
// Icon
|
|
247
250
|
updateIconPreview(currentProject ? currentProject.icon : null);
|
|
248
251
|
|
|
252
|
+
populateProjectSessionVisibilitySettings(currentSlug, currentProject);
|
|
253
|
+
|
|
249
254
|
// Owner (only in multi-user mode)
|
|
250
255
|
var ownerField = document.getElementById("ps-owner-field");
|
|
251
256
|
if (ownerField) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// sidebar-projects.js - Project icon strip, context menus, emoji picker, drag-and-drop, worktree modal
|
|
2
2
|
// Extracted from sidebar.js (PR-36)
|
|
3
3
|
|
|
4
|
-
import { escapeHtml } from './utils.js';
|
|
4
|
+
import { escapeHtml, showToast } from './utils.js';
|
|
5
5
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
6
6
|
import { openProjectSettings } from './project-settings.js';
|
|
7
7
|
import { triggerShare } from './qrcode.js';
|
|
@@ -415,6 +415,11 @@ function showProjectCtxMenu(anchorEl, slug, name, icon, position) {
|
|
|
415
415
|
closeProjectCtxMenu();
|
|
416
416
|
if (closeUserCtxMenu) closeUserCtxMenu();
|
|
417
417
|
closeEmojiPicker();
|
|
418
|
+
var projects = getCachedProjects();
|
|
419
|
+
var targetProject = null;
|
|
420
|
+
for (var projectIndex = 0; projectIndex < projects.length; projectIndex++) {
|
|
421
|
+
if (projects[projectIndex].slug === slug) { targetProject = projects[projectIndex]; break; }
|
|
422
|
+
}
|
|
418
423
|
|
|
419
424
|
var menu = document.createElement("div");
|
|
420
425
|
menu.className = "project-ctx-menu";
|
|
@@ -438,7 +443,13 @@ function showProjectCtxMenu(anchorEl, slug, name, icon, position) {
|
|
|
438
443
|
settingsItem.addEventListener("click", function (e) {
|
|
439
444
|
e.stopPropagation();
|
|
440
445
|
closeProjectCtxMenu();
|
|
441
|
-
|
|
446
|
+
if (!targetProject) {
|
|
447
|
+
showToast("Project details are unavailable. Refresh and try again.", "error");
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
openProjectSettings(slug, Object.assign({}, targetProject, {
|
|
451
|
+
name: targetProject.title || targetProject.project || name,
|
|
452
|
+
}));
|
|
442
453
|
});
|
|
443
454
|
menu.appendChild(settingsItem);
|
|
444
455
|
}
|
|
@@ -460,7 +471,7 @@ function showProjectCtxMenu(anchorEl, slug, name, icon, position) {
|
|
|
460
471
|
|
|
461
472
|
// --- Manage Access ---
|
|
462
473
|
if (store.get('isMultiUserMode')) {
|
|
463
|
-
var isProjectOwner = store.get('myUserId') &&
|
|
474
|
+
var isProjectOwner = store.get('myUserId') && targetProject && targetProject.projectOwnerId && store.get('myUserId') === targetProject.projectOwnerId;
|
|
464
475
|
var isAdmin = store.get('permissions') && store.get('permissions').projectSettings !== false;
|
|
465
476
|
if (isProjectOwner || isAdmin) {
|
|
466
477
|
var accessItem = document.createElement("button");
|
package/lib/server.js
CHANGED
|
@@ -158,6 +158,7 @@ function createServer(opts) {
|
|
|
158
158
|
var onReorderProjects = opts.onReorderProjects || null;
|
|
159
159
|
var onSetProjectTitle = opts.onSetProjectTitle || null;
|
|
160
160
|
var onSetProjectIcon = opts.onSetProjectIcon || null;
|
|
161
|
+
var onSetProjectSessionVisibilityDefault = opts.onSetProjectSessionVisibilityDefault || null;
|
|
161
162
|
var onProjectOwnerChanged = opts.onProjectOwnerChanged || null;
|
|
162
163
|
var onGetServerDefaultEffort = opts.onGetServerDefaultEffort || null;
|
|
163
164
|
var onSetServerDefaultEffort = opts.onSetServerDefaultEffort || null;
|
|
@@ -1458,6 +1459,7 @@ function createServer(opts) {
|
|
|
1458
1459
|
onReorderProjects: onReorderProjects,
|
|
1459
1460
|
onSetProjectTitle: onSetProjectTitle,
|
|
1460
1461
|
onSetProjectIcon: onSetProjectIcon,
|
|
1462
|
+
onSetProjectSessionVisibilityDefault: onSetProjectSessionVisibilityDefault,
|
|
1461
1463
|
onProjectOwnerChanged: onProjectOwnerChanged,
|
|
1462
1464
|
onGetServerDefaultEffort: onGetServerDefaultEffort,
|
|
1463
1465
|
onSetServerDefaultEffort: onSetServerDefaultEffort,
|
|
@@ -1707,7 +1709,12 @@ function createServer(opts) {
|
|
|
1707
1709
|
var visible = [];
|
|
1708
1710
|
projects.forEach(function (ctx, slug) {
|
|
1709
1711
|
if (users.isMultiUser() && (!userId || !canUserAccessSlug(userId, slug))) return;
|
|
1710
|
-
|
|
1712
|
+
var status = ctx.getStatus();
|
|
1713
|
+
if (!status.isMate && onGetProjectAccess) {
|
|
1714
|
+
var access = onGetProjectAccess(slug);
|
|
1715
|
+
status.sessionVisibilityDefault = access && access.sessionVisibilityDefault === "shared" ? "shared" : "private";
|
|
1716
|
+
}
|
|
1717
|
+
visible.push(status);
|
|
1711
1718
|
});
|
|
1712
1719
|
return visible;
|
|
1713
1720
|
}
|
|
@@ -7,6 +7,66 @@ function restore(meta) {
|
|
|
7
7
|
return meta.sessionVisibilityExplicit === true ? normalize(meta.sessionVisibility) : "private";
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
function defaultForProject(access) {
|
|
11
|
+
return access && access.sessionVisibilityDefault === "shared" ? "shared" : "private";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function resolveNewSessionVisibility(access, msg, isMate) {
|
|
15
|
+
if (isMate === true) return { visibility: "private" };
|
|
16
|
+
if (!msg || !Object.prototype.hasOwnProperty.call(msg, "sessionVisibility")) {
|
|
17
|
+
return { visibility: defaultForProject(access) };
|
|
18
|
+
}
|
|
19
|
+
if (msg.sessionVisibility === "private" || msg.sessionVisibility === "shared") {
|
|
20
|
+
return { visibility: msg.sessionVisibility };
|
|
21
|
+
}
|
|
22
|
+
return { error: "Session visibility must be private or shared." };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function defaultChangeResult(ctx, ws, msg, result) {
|
|
26
|
+
var requestedSlug = typeof msg.slug === "string" && msg.slug.length > 0 && msg.slug.length <= 200 ? msg.slug : ctx.slug;
|
|
27
|
+
ctx.sendTo(ws, Object.assign({
|
|
28
|
+
type: "set_project_session_visibility_default_result",
|
|
29
|
+
slug: requestedSlug,
|
|
30
|
+
requestId: typeof msg.requestId === "string" ? msg.requestId.substring(0, 200) : null,
|
|
31
|
+
}, result));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function handleDefaultChange(ctx, ws, msg) {
|
|
35
|
+
if (!msg || (msg.visibility !== "shared" && msg.visibility !== "private")) {
|
|
36
|
+
defaultChangeResult(ctx, ws, msg || {}, { ok: false, error: "Session visibility default must be private or shared." });
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
if (msg.slug && msg.slug !== ctx.slug) {
|
|
40
|
+
defaultChangeResult(ctx, ws, msg, { ok: false, error: "Project settings access is not permitted" });
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
if (ctx.isMate === true || !ctx.usersModule || !ctx.usersModule.isMultiUser || !ctx.usersModule.isMultiUser()) {
|
|
44
|
+
defaultChangeResult(ctx, ws, msg, { ok: false, error: "Not supported" });
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
var user = ws && ws._clayUser && ctx.usersModule.findUserById(ws._clayUser.id);
|
|
48
|
+
var access = ctx.getProjectAccess ? ctx.getProjectAccess() : null;
|
|
49
|
+
if (access && access.isWorktree === true) {
|
|
50
|
+
defaultChangeResult(ctx, ws, msg, { ok: false, error: "Worktrees inherit this setting from their parent project." });
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
var permissions = user && ctx.usersModule.getEffectivePermissions
|
|
54
|
+
? ctx.usersModule.getEffectivePermissions(user, ctx.osUsers) : {};
|
|
55
|
+
var isOwner = !!(user && access && access.ownerId && access.ownerId === user.id);
|
|
56
|
+
var isAdmin = !!(user && user.role === "admin");
|
|
57
|
+
if (!user || !permissions.projectSettings || (!isOwner && !isAdmin)) {
|
|
58
|
+
defaultChangeResult(ctx, ws, msg, { ok: false, error: "Project settings access is not permitted" });
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
if (typeof ctx.opts.onSetProjectSessionVisibilityDefault !== "function") {
|
|
62
|
+
defaultChangeResult(ctx, ws, msg, { ok: false, error: "Not supported" });
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
var result = ctx.opts.onSetProjectSessionVisibilityDefault(ctx.slug, msg.visibility);
|
|
66
|
+
defaultChangeResult(ctx, ws, msg, { ok: !!(result && result.ok), visibility: result && result.visibility, error: result && result.error });
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
10
70
|
function handleChange(ctx, ws, msg) {
|
|
11
71
|
if (typeof msg.sessionId !== "number" || (msg.visibility !== "shared" && msg.visibility !== "private")) return;
|
|
12
72
|
var session = ctx.sm.sessions.get(msg.sessionId);
|
|
@@ -38,4 +98,5 @@ function revokeViewers(ctx, session) {
|
|
|
38
98
|
}
|
|
39
99
|
}
|
|
40
100
|
|
|
41
|
-
module.exports = { normalize: normalize, restore: restore,
|
|
101
|
+
module.exports = { normalize: normalize, restore: restore, defaultForProject: defaultForProject, resolveNewSessionVisibility: resolveNewSessionVisibility,
|
|
102
|
+
handleChange: handleChange, handleDefaultChange: handleDefaultChange, revokeViewers: revokeViewers };
|
package/lib/sessions.js
CHANGED
|
@@ -1100,6 +1100,8 @@ function createSessionManager(opts) {
|
|
|
1100
1100
|
lastActivity: Date.now(),
|
|
1101
1101
|
history: cliHistory,
|
|
1102
1102
|
messageUUIDs: [],
|
|
1103
|
+
sessionVisibility: sessionVisibility.normalize(opts && opts.sessionVisibility),
|
|
1104
|
+
sessionVisibilityExplicit: !!(opts && opts.sessionVisibilityExplicit === true),
|
|
1103
1105
|
bookmarked: false,
|
|
1104
1106
|
favoriteOrder: null,
|
|
1105
1107
|
};
|
package/lib/ws-schema.js
CHANGED
|
@@ -27,6 +27,8 @@ var schema = {
|
|
|
27
27
|
"reorder_session_bookmarks": { direction: "c2s", handler: "lib/project-sessions.js", description: "Reorder favorited sessions within the favorites area" },
|
|
28
28
|
"bulk_delete_sessions": { direction: "c2s", handler: "lib/project-sessions.js", description: "Delete a group of sessions at once" },
|
|
29
29
|
"set_session_visibility": { direction: "c2s", handler: "lib/project-sessions.js", description: "Show or hide a session in the sidebar" },
|
|
30
|
+
"set_project_session_visibility_default": { direction: "c2s", handler: "lib/project-sessions.js", description: "Set private/shared default for new ordinary project sessions" },
|
|
31
|
+
"set_project_session_visibility_default_result": { direction: "s2c", handler: "lib/public/modules/project-session-visibility-settings.js", description: "Correlated project session visibility default update result" },
|
|
30
32
|
"search_sessions": { direction: "c2s", handler: "lib/project-sessions.js", description: "Search session titles" },
|
|
31
33
|
"search_session_content": { direction: "c2s", handler: "lib/project-sessions.js", description: "Full-text search within a session" },
|
|
32
34
|
"load_more_history": { direction: "c2s", handler: "lib/project-sessions.js", description: "Request older history entries for the current session" },
|
package/package.json
CHANGED