clay-server 3.8.1 → 4.0.0-beta.10
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/ask-user-mcp-server.js +5 -119
- package/lib/background-task-timing.js +105 -0
- package/lib/builtin-mates.js +6 -6
- package/lib/claude-hook-installer.js +15 -0
- package/lib/clay-history-mcp-server.js +62 -297
- package/lib/daemon.js +23 -1
- package/lib/debate-model-selection.js +119 -0
- package/lib/home-debate-tool-policy.js +164 -0
- package/lib/home-response-notifications.js +26 -0
- package/lib/knowledge-import.js +436 -0
- package/lib/knowledge-record-store.js +188 -0
- package/lib/knowledge-search.js +224 -0
- package/lib/mate-creation-mcp-server.js +55 -0
- package/lib/mate-knowledge-mcp-server.js +151 -0
- package/lib/mate-knowledge-migration.js +283 -0
- package/lib/mate-knowledge-service.js +488 -0
- package/lib/mate-knowledge-sync.js +482 -0
- package/lib/mate-ready-creation.js +95 -0
- package/lib/mates.js +2 -0
- package/lib/notes.js +2 -1
- package/lib/project-ask-user.js +64 -0
- package/lib/project-capsule-catalog.js +101 -0
- package/lib/project-connection.js +17 -0
- package/lib/project-debate-proposal.js +133 -17
- package/lib/project-debate.js +214 -110
- package/lib/project-delegated-follow-up.js +84 -0
- package/lib/project-delegated-session.js +53 -0
- package/lib/project-filesystem.js +7 -9
- package/lib/project-knowledge.js +23 -0
- package/lib/project-logs-comments.js +137 -0
- package/lib/project-logs-mcp-server.js +290 -0
- package/lib/project-logs-query.js +167 -0
- package/lib/project-logs-root.js +67 -0
- package/lib/project-logs-schema.js +259 -0
- package/lib/project-logs-service.js +357 -0
- package/lib/project-logs-snapshot.js +202 -0
- package/lib/project-logs-store.js +498 -0
- package/lib/project-logs-versioning.js +128 -0
- package/lib/project-logs.js +347 -0
- package/lib/project-mate-creation-proposal.js +120 -0
- package/lib/project-mate-interaction.js +14 -0
- package/lib/project-mate-knowledge.js +110 -0
- package/lib/project-memory.js +36 -7
- package/lib/project-models.js +157 -32
- package/lib/project-session-notes.js +2 -2
- package/lib/project-session-pair.js +8 -8
- package/lib/project-sessions.js +66 -51
- package/lib/project-vendor-login.js +414 -0
- package/lib/project-worker-proposal.js +25 -25
- package/lib/project-workspace-query.js +144 -0
- package/lib/project.js +433 -102
- package/lib/public/app.js +109 -48
- package/lib/public/css/avatar-imprints.css +0 -1
- package/lib/public/css/base.css +4 -0
- package/lib/public/css/capsule-ui.css +389 -0
- package/lib/public/css/command-palette.css +306 -6
- package/lib/public/css/home-capsule-library.css +243 -0
- package/lib/public/css/home-capsule-source.css +192 -0
- package/lib/public/css/home-debate-live.css +146 -0
- package/lib/public/css/home-debate-planning.css +125 -0
- package/lib/public/css/home-debates-archive.css +281 -0
- package/lib/public/css/home-hub.css +896 -405
- package/lib/public/css/home-mate-creation.css +34 -0
- package/lib/public/css/home-mate-model-picker.css +131 -0
- package/lib/public/css/home-mate-settings.css +344 -0
- package/lib/public/css/home-session-actions.css +265 -0
- package/lib/public/css/home-sidebar.css +667 -0
- package/lib/public/css/icon-strip.css +1 -2
- package/lib/public/css/input.css +79 -25
- package/lib/public/css/mates.css +7 -34
- package/lib/public/css/pane.css +1 -1
- package/lib/public/css/playbook.css +0 -80
- package/lib/public/css/project-logs.css +234 -0
- package/lib/public/css/scheduler.css +1 -1
- package/lib/public/css/sticky-notes.css +53 -117
- package/lib/public/css/tui-attention.css +0 -44
- package/lib/public/css/workspace-assignment.css +45 -0
- package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
- package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
- package/lib/public/index.html +96 -74
- package/lib/public/modules/app-connection.js +100 -5
- package/lib/public/modules/app-dm.js +43 -160
- package/lib/public/modules/app-home-hub.js +328 -585
- package/lib/public/modules/app-message-cards.js +242 -0
- package/lib/public/modules/app-message-router.js +84 -0
- package/lib/public/modules/app-messages.js +124 -38
- package/lib/public/modules/app-notifications.js +8 -119
- package/lib/public/modules/app-panels.js +43 -2
- package/lib/public/modules/app-projects.js +9 -1
- package/lib/public/modules/app-rendering.js +21 -602
- package/lib/public/modules/avatar-imprint.js +11 -8
- package/lib/public/modules/background-tasks-ui.js +139 -16
- package/lib/public/modules/chat-bubble-renderer.js +152 -0
- package/lib/public/modules/chat-render-runtime.js +198 -0
- package/lib/public/modules/command-palette.js +225 -436
- package/lib/public/modules/dm-render.js +82 -0
- package/lib/public/modules/home-capsule-creation-intent.js +31 -0
- package/lib/public/modules/home-capsule-library.js +146 -0
- package/lib/public/modules/home-capsule-source.js +383 -0
- package/lib/public/modules/home-chat-identity.js +35 -0
- package/lib/public/modules/home-chat-scroll.js +77 -0
- package/lib/public/modules/home-chat-stream-state.js +34 -0
- package/lib/public/modules/home-composer-focus.js +25 -0
- package/lib/public/modules/home-conversations-sheet.js +179 -0
- package/lib/public/modules/home-debate-activity.js +48 -0
- package/lib/public/modules/home-debate-controls.js +295 -0
- package/lib/public/modules/home-debate-launch.js +41 -0
- package/lib/public/modules/home-debate-live.js +284 -0
- package/lib/public/modules/home-debate-models.js +73 -0
- package/lib/public/modules/home-debate-planning.js +335 -0
- package/lib/public/modules/home-debates-archive.js +279 -0
- package/lib/public/modules/home-dock-resize.js +74 -0
- package/lib/public/modules/home-dock.js +446 -0
- package/lib/public/modules/home-mate-chat.js +496 -0
- package/lib/public/modules/home-mate-creation.js +109 -0
- package/lib/public/modules/home-mate-model-picker.js +281 -0
- package/lib/public/modules/home-mate-selection.js +38 -0
- package/lib/public/modules/home-mate-settings-menu.js +131 -0
- package/lib/public/modules/home-mate-settings.js +383 -0
- package/lib/public/modules/home-session-actions.js +373 -0
- package/lib/public/modules/home-session-model-confirmation.js +13 -0
- package/lib/public/modules/home-shell.js +9 -0
- package/lib/public/modules/home-sidebar-chat-list.js +63 -0
- package/lib/public/modules/home-sidebar.js +165 -0
- package/lib/public/modules/home-sub-surface.js +19 -0
- package/lib/public/modules/home-surface-boot.js +68 -0
- package/lib/public/modules/home-surface.js +141 -0
- package/lib/public/modules/home-tools.js +271 -0
- package/lib/public/modules/markdown.js +50 -2
- package/lib/public/modules/mate-management.js +86 -0
- package/lib/public/modules/mate-sidebar.js +0 -8
- package/lib/public/modules/mate-wizard.js +0 -7
- package/lib/public/modules/mention.js +17 -19
- package/lib/public/modules/pane-bridge.js +9 -0
- package/lib/public/modules/profile.js +4 -5
- package/lib/public/modules/project-logs-ambient.js +238 -0
- package/lib/public/modules/project-logs-render.js +359 -0
- package/lib/public/modules/project-logs.js +328 -0
- package/lib/public/modules/project-settings.js +34 -28
- package/lib/public/modules/project-switcher.js +11 -5
- package/lib/public/modules/project-workspace-assignment.js +23 -0
- package/lib/public/modules/scheduler.js +17 -3
- package/lib/public/modules/search-clay-chat.js +402 -0
- package/lib/public/modules/server-settings.js +14 -12
- package/lib/public/modules/session-actions.js +1 -1
- package/lib/public/modules/sidebar-mates.js +56 -552
- package/lib/public/modules/sidebar-mobile.js +12 -73
- package/lib/public/modules/sidebar-presence.js +37 -0
- package/lib/public/modules/sidebar-sessions.js +25 -1
- package/lib/public/modules/split-pair-ui.js +13 -13
- package/lib/public/modules/split-view.js +7 -0
- package/lib/public/modules/sticky-notes.js +106 -53
- package/lib/public/modules/tool-input-composition.js +48 -0
- package/lib/public/modules/tool-llm-status.js +167 -0
- package/lib/public/modules/tool-palette.js +76 -4
- package/lib/public/modules/tool-renderer-advanced.js +285 -0
- package/lib/public/modules/tool-renderer-chart.js +158 -0
- package/lib/public/modules/tool-renderer-semantics.js +35 -0
- package/lib/public/modules/tool-renderer.js +464 -0
- package/lib/public/modules/tool-runtime.js +309 -0
- package/lib/public/modules/tool-ui-evaluator.js +97 -0
- package/lib/public/modules/tool-ui-tree.js +21 -0
- package/lib/public/modules/tui-attention.js +10 -0
- package/lib/public/modules/user-settings.js +16 -23
- package/lib/public/modules/vendor-login.js +287 -0
- package/lib/public/modules/worker-proposal.js +2 -2
- package/lib/public/modules/workspace-assignment-card.js +149 -0
- package/lib/public/modules/workspace-assignment-routing.js +4 -0
- package/lib/public/style.css +14 -2
- package/lib/runtime-env.js +65 -0
- package/lib/sdk-bridge.js +120 -32
- package/lib/sdk-message-processor.js +34 -13
- package/lib/server-home-capsule-creation.js +36 -0
- package/lib/server-home-chat-events.js +287 -0
- package/lib/server-home-chat.js +518 -0
- package/lib/server-home-clay-entry.js +65 -0
- package/lib/server-home-clay-session-links.js +26 -0
- package/lib/server-home-debate-planning.js +214 -0
- package/lib/server-home-debates.js +137 -0
- package/lib/server-home-mate-creation.js +261 -0
- package/lib/server-home-models.js +241 -0
- package/lib/server-home-preferences.js +61 -0
- package/lib/server-mates.js +37 -0
- package/lib/server-tools.js +377 -0
- package/lib/server.js +166 -59
- package/lib/session-pair-mcp-server.js +6 -6
- package/lib/sessions.js +22 -4
- package/lib/tool-capsule-source.js +142 -0
- package/lib/tool-control-mcp-server.js +126 -0
- package/lib/tool-llm.js +48 -0
- package/lib/tool-storage.js +77 -0
- package/lib/tool-ui-spec-advanced.js +82 -0
- package/lib/tool-ui-spec.js +261 -0
- package/lib/tools-registry.js +286 -0
- package/lib/users-home-dock-preferences.js +77 -0
- package/lib/users-home-surface-preferences.js +99 -0
- package/lib/users-preferences.js +4 -7
- package/lib/users.js +12 -0
- package/lib/whats-new-content.js +3 -3
- package/lib/workspace-assignment-service.js +420 -0
- package/lib/workspace-query-mcp-server.js +154 -0
- package/lib/workspace-query-service.js +492 -0
- package/lib/ws-schema.js +107 -12
- package/lib/yoke/adapters/acp.js +2 -1
- package/lib/yoke/adapters/antigravity.js +2 -1
- package/lib/yoke/adapters/claude-worker.js +11 -4
- package/lib/yoke/adapters/claude.js +141 -46
- package/lib/yoke/adapters/codex.js +79 -11
- package/lib/yoke/adapters/kiro.js +2 -1
- package/lib/yoke/claude-user-input.js +38 -0
- package/lib/yoke/codex-app-server.js +20 -2
- package/lib/yoke/codex-background-tasks.js +8 -2
- package/lib/yoke/codex-user-input.js +64 -0
- package/lib/yoke/complete-once.js +164 -0
- package/lib/yoke/index.js +5 -1
- package/lib/yoke/interface.js +19 -0
- package/lib/yoke/mcp-bridge-server.js +2 -6
- package/lib/yoke/user-input.js +254 -0
- package/package.json +4 -3
- package/lib/public/css/home-chat.css +0 -380
- package/lib/public/modules/home-chat.js +0 -438
- package/lib/server-clay-home.js +0 -245
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
// Project Logs — the project's ledger of agent-authored decisions and work.
|
|
2
|
+
//
|
|
3
|
+
// A bounded right-side workbench pane, like the document viewer and terminal.
|
|
4
|
+
// Inside it there is one navigation stack rather than a master/detail split:
|
|
5
|
+
// the list fills the pane, opening an entry replaces it with the full-pane
|
|
6
|
+
// record, and Back returns to the list with the query, filter, and scroll
|
|
7
|
+
// position intact.
|
|
8
|
+
//
|
|
9
|
+
// People read and comment. Canonical entries are created and revised only by
|
|
10
|
+
// this project's agent sessions through the clay-logs MCP tools, so this
|
|
11
|
+
// module has no create or edit affordance at all.
|
|
12
|
+
|
|
13
|
+
import { store } from './store.js';
|
|
14
|
+
import { getWs } from './ws-ref.js';
|
|
15
|
+
import { refreshIcons } from './icons.js';
|
|
16
|
+
import { showToast } from './utils.js';
|
|
17
|
+
import { closeScheduler } from './scheduler.js';
|
|
18
|
+
import { closeArchive, hideNotes } from './sticky-notes.js';
|
|
19
|
+
import { closeFileViewer } from './filebrowser.js';
|
|
20
|
+
import { closeTerminal } from './terminal.js';
|
|
21
|
+
import { renderFilter, renderList, renderDetail } from './project-logs-render.js';
|
|
22
|
+
import {
|
|
23
|
+
initProjectLogsAmbient, bindPanelHover, noteCanonicalUpdate,
|
|
24
|
+
acknowledgeUpdates, resetAmbient, syncAmbient, hidePreview, pinFromPreview
|
|
25
|
+
} from './project-logs-ambient.js';
|
|
26
|
+
|
|
27
|
+
var panel = null;
|
|
28
|
+
var listEl = null;
|
|
29
|
+
var detailEl = null;
|
|
30
|
+
var searchEl = null;
|
|
31
|
+
var filterEl = null;
|
|
32
|
+
var backBtn = null;
|
|
33
|
+
var searchTimer = null;
|
|
34
|
+
var requestCounter = 0;
|
|
35
|
+
|
|
36
|
+
function nextRequestId(prefix) {
|
|
37
|
+
requestCounter += 1;
|
|
38
|
+
return prefix + "-" + Date.now() + "-" + requestCounter;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function send(message) {
|
|
42
|
+
var ws = getWs();
|
|
43
|
+
if (!ws || ws.readyState !== 1) return false;
|
|
44
|
+
ws.send(JSON.stringify(message));
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function ensurePanel() {
|
|
49
|
+
if (panel) return;
|
|
50
|
+
var panels = document.getElementById("main-panels");
|
|
51
|
+
if (!panels) return;
|
|
52
|
+
panel = document.createElement("section");
|
|
53
|
+
panel.id = "project-logs-panel";
|
|
54
|
+
panel.className = "project-logs-panel hidden";
|
|
55
|
+
panel.setAttribute("aria-label", "Project Logs");
|
|
56
|
+
panel.innerHTML =
|
|
57
|
+
'<header class="project-logs-topbar">' +
|
|
58
|
+
'<button id="project-logs-back" class="project-logs-icon-btn hidden" type="button" title="Back to the log list" aria-label="Back to the log list"><i data-lucide="arrow-left"></i></button>' +
|
|
59
|
+
'<span class="project-logs-title"><i data-lucide="notebook-tabs"></i>Logs</span>' +
|
|
60
|
+
'<span class="project-logs-subtitle">Decisions and work recorded by this project</span>' +
|
|
61
|
+
'<div class="project-logs-window-actions" aria-label="Project Logs window controls">' +
|
|
62
|
+
'<button id="project-logs-wide" class="project-logs-icon-btn" type="button" title="Widen panel" aria-label="Widen Logs panel" aria-pressed="false"><i data-lucide="chevrons-left-right"></i></button>' +
|
|
63
|
+
'<button id="project-logs-fullscreen" class="project-logs-icon-btn" type="button" title="Toggle fullscreen" aria-label="Toggle Logs fullscreen" aria-pressed="false"><i data-lucide="maximize-2"></i></button>' +
|
|
64
|
+
'<button id="project-logs-close" class="project-logs-icon-btn" type="button" title="Close Logs" aria-label="Close Logs"><i data-lucide="x"></i></button>' +
|
|
65
|
+
'</div>' +
|
|
66
|
+
'</header>' +
|
|
67
|
+
'<div id="project-logs-toolbar" class="project-logs-toolbar">' +
|
|
68
|
+
'<label class="project-logs-search"><i data-lucide="search"></i><input id="project-logs-search" type="search" placeholder="Search logs" autocomplete="off"></label>' +
|
|
69
|
+
'<div id="project-logs-filter-slot"></div>' +
|
|
70
|
+
'</div>' +
|
|
71
|
+
'<div id="project-logs-list" class="project-logs-list" role="list"></div>' +
|
|
72
|
+
'<main id="project-logs-detail" class="project-logs-detail hidden"></main>';
|
|
73
|
+
panels.appendChild(panel);
|
|
74
|
+
|
|
75
|
+
listEl = panel.querySelector("#project-logs-list");
|
|
76
|
+
detailEl = panel.querySelector("#project-logs-detail");
|
|
77
|
+
searchEl = panel.querySelector("#project-logs-search");
|
|
78
|
+
filterEl = panel.querySelector("#project-logs-filter-slot");
|
|
79
|
+
backBtn = panel.querySelector("#project-logs-back");
|
|
80
|
+
|
|
81
|
+
panel.querySelector("#project-logs-close").addEventListener("click", closeProjectLogs);
|
|
82
|
+
backBtn.addEventListener("click", function () { pinOnInteraction(); showList(); });
|
|
83
|
+
// Any meaningful interaction inside a preview commits to it.
|
|
84
|
+
searchEl.addEventListener("focus", pinOnInteraction);
|
|
85
|
+
panel.addEventListener("pointerdown", pinOnInteraction);
|
|
86
|
+
bindPanelHover(panel);
|
|
87
|
+
panel.querySelector("#project-logs-wide").addEventListener("click", function () {
|
|
88
|
+
applyWindowState(!store.get('projectLogsWide'), store.get('projectLogsFullscreen'));
|
|
89
|
+
});
|
|
90
|
+
panel.querySelector("#project-logs-fullscreen").addEventListener("click", function () {
|
|
91
|
+
applyWindowState(store.get('projectLogsWide'), !store.get('projectLogsFullscreen'));
|
|
92
|
+
});
|
|
93
|
+
searchEl.addEventListener("input", function () {
|
|
94
|
+
if (searchTimer) clearTimeout(searchTimer);
|
|
95
|
+
searchTimer = setTimeout(requestList, 180);
|
|
96
|
+
});
|
|
97
|
+
refreshIcons();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// A preview reveals the pane without committing to it: no acknowledgement, no
|
|
101
|
+
// open state, and no list refresh beyond what is already on screen.
|
|
102
|
+
function revealPreview() {
|
|
103
|
+
ensurePanel();
|
|
104
|
+
if (!panel) return;
|
|
105
|
+
panel.classList.remove("hidden");
|
|
106
|
+
panel.classList.add("project-logs-previewing");
|
|
107
|
+
applyWindowState(store.get('projectLogsWide'), false);
|
|
108
|
+
if (!store.get('projectLogsEntries') || !store.get('projectLogsEntries').length) requestList();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function hideRevealedPreview() {
|
|
112
|
+
if (!panel) return;
|
|
113
|
+
panel.classList.remove("project-logs-previewing");
|
|
114
|
+
if (store.get('projectLogsOpen')) return;
|
|
115
|
+
panel.classList.add("hidden");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Width and fullscreen are transient view state, so they live in the store like
|
|
119
|
+
// every other mutable UI value. `panel-fullscreen` is the shared class the
|
|
120
|
+
// document viewer and terminal already use.
|
|
121
|
+
function applyWindowState(wide, fullscreen) {
|
|
122
|
+
store.set({ projectLogsWide: !!wide, projectLogsFullscreen: !!fullscreen });
|
|
123
|
+
if (!panel) return;
|
|
124
|
+
panel.classList.toggle("project-logs-wide", !!wide && !fullscreen);
|
|
125
|
+
panel.classList.toggle("panel-fullscreen", !!fullscreen);
|
|
126
|
+
var wideBtn = panel.querySelector("#project-logs-wide");
|
|
127
|
+
var fullBtn = panel.querySelector("#project-logs-fullscreen");
|
|
128
|
+
if (wideBtn) {
|
|
129
|
+
wideBtn.setAttribute("aria-pressed", wide ? "true" : "false");
|
|
130
|
+
wideBtn.disabled = !!fullscreen;
|
|
131
|
+
}
|
|
132
|
+
if (fullBtn) fullBtn.setAttribute("aria-pressed", fullscreen ? "true" : "false");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// --- Navigation stack ----------------------------------------------------
|
|
136
|
+
|
|
137
|
+
function showList() {
|
|
138
|
+
if (!panel) return;
|
|
139
|
+
store.set({ projectLogsView: "list", projectLogsSelectedRef: null });
|
|
140
|
+
detailEl.classList.add("hidden");
|
|
141
|
+
listEl.classList.remove("hidden");
|
|
142
|
+
panel.querySelector("#project-logs-toolbar").classList.remove("hidden");
|
|
143
|
+
backBtn.classList.add("hidden");
|
|
144
|
+
// The list is re-shown rather than rebuilt, so the query and filter survive;
|
|
145
|
+
// the scroll offset is restored from where the user left it.
|
|
146
|
+
listEl.scrollTop = store.get('projectLogsListScroll') || 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// A preview becomes a real open the moment the user does something with it.
|
|
150
|
+
function pinOnInteraction() {
|
|
151
|
+
if (store.get('projectLogsOpen')) return;
|
|
152
|
+
if (!store.get('projectLogsPreview')) return;
|
|
153
|
+
pinFromPreview();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function showDetail(entry) {
|
|
157
|
+
if (!panel || !entry) return;
|
|
158
|
+
store.set({ projectLogsListScroll: listEl.scrollTop, projectLogsView: "detail", projectLogsSelectedRef: entry.ref });
|
|
159
|
+
listEl.classList.add("hidden");
|
|
160
|
+
panel.querySelector("#project-logs-toolbar").classList.add("hidden");
|
|
161
|
+
detailEl.classList.remove("hidden");
|
|
162
|
+
detailEl.scrollTop = 0;
|
|
163
|
+
backBtn.classList.remove("hidden");
|
|
164
|
+
renderDetail(detailEl, entry, { onComment: submitComment });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// --- Requests ------------------------------------------------------------
|
|
168
|
+
|
|
169
|
+
function requestList() {
|
|
170
|
+
var requestId = nextRequestId("list-logs");
|
|
171
|
+
store.set({ projectLogsListRequestId: requestId });
|
|
172
|
+
send({
|
|
173
|
+
type: "project_logs_list",
|
|
174
|
+
requestId: requestId,
|
|
175
|
+
query: searchEl ? searchEl.value.trim() : "",
|
|
176
|
+
category: store.get('projectLogsCategory') || "",
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function requestEntry(ref) {
|
|
181
|
+
var requestId = nextRequestId("read-log");
|
|
182
|
+
store.set({ projectLogsReadRequestId: requestId });
|
|
183
|
+
send({ type: "project_log_read", requestId: requestId, ref: ref });
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function submitComment(ref, body, statusEl, inputEl) {
|
|
187
|
+
pinOnInteraction();
|
|
188
|
+
var requestId = nextRequestId("comment-log");
|
|
189
|
+
store.set({ projectLogsCommentRequestId: requestId, projectLogsCommentInput: inputEl || null });
|
|
190
|
+
if (!send({ type: "project_log_comment", requestId: requestId, ref: ref, body: body })) {
|
|
191
|
+
statusEl.textContent = "Logs are unavailable while disconnected.";
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
statusEl.textContent = "Posting...";
|
|
195
|
+
store.set({ projectLogsCommentStatusEl: statusEl });
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function applyFilter(category) {
|
|
199
|
+
pinOnInteraction();
|
|
200
|
+
store.set({ projectLogsCategory: category || "" });
|
|
201
|
+
requestList();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// --- Lifecycle -----------------------------------------------------------
|
|
205
|
+
|
|
206
|
+
export function initProjectLogs() {
|
|
207
|
+
var button = document.getElementById("project-logs-btn");
|
|
208
|
+
if (!button) return;
|
|
209
|
+
button.addEventListener("click", function () {
|
|
210
|
+
if (store.get('projectLogsOpen')) closeProjectLogs();
|
|
211
|
+
else openProjectLogs();
|
|
212
|
+
});
|
|
213
|
+
initProjectLogsAmbient({
|
|
214
|
+
reveal: revealPreview,
|
|
215
|
+
hide: hideRevealedPreview,
|
|
216
|
+
pin: openProjectLogs,
|
|
217
|
+
});
|
|
218
|
+
store.subscribe(function (state, previous) {
|
|
219
|
+
if (state.currentSlug === previous.currentSlug) return;
|
|
220
|
+
closeProjectLogs();
|
|
221
|
+
applyWindowState(false, false);
|
|
222
|
+
// A different project must never inherit the previous project's unread
|
|
223
|
+
// state or animate its updates.
|
|
224
|
+
resetAmbient();
|
|
225
|
+
store.set({
|
|
226
|
+
projectLogsEntries: [],
|
|
227
|
+
projectLogsSelectedRef: null,
|
|
228
|
+
projectLogsView: "list",
|
|
229
|
+
projectLogsCategory: "",
|
|
230
|
+
projectLogsListScroll: 0,
|
|
231
|
+
projectLogsListRequestId: null,
|
|
232
|
+
projectLogsReadRequestId: null,
|
|
233
|
+
projectLogsCommentRequestId: null
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// The conversation, composer, and title bar stay mounted and visible: Logs
|
|
239
|
+
// opens beside them, it does not replace them.
|
|
240
|
+
export function openProjectLogs() {
|
|
241
|
+
ensurePanel();
|
|
242
|
+
if (!panel) return;
|
|
243
|
+
closeScheduler();
|
|
244
|
+
closeArchive();
|
|
245
|
+
hideNotes();
|
|
246
|
+
// Claim the single right workbench slot.
|
|
247
|
+
try { closeFileViewer(); } catch (e) {}
|
|
248
|
+
try { closeTerminal(); } catch (e) {}
|
|
249
|
+
panel.classList.remove("hidden");
|
|
250
|
+
panel.classList.remove("project-logs-previewing");
|
|
251
|
+
applyWindowState(store.get('projectLogsWide'), false);
|
|
252
|
+
var button = document.getElementById("project-logs-btn");
|
|
253
|
+
if (button) button.classList.add("active");
|
|
254
|
+
store.set({ projectLogsOpen: true, projectLogsPinned: true, projectLogsPreview: false });
|
|
255
|
+
// Opening the ledger is the acknowledgement; the ambient cues clear here and
|
|
256
|
+
// nowhere else.
|
|
257
|
+
acknowledgeUpdates();
|
|
258
|
+
showList();
|
|
259
|
+
requestList();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export function closeProjectLogs() {
|
|
263
|
+
hidePreview();
|
|
264
|
+
if (!store.get('projectLogsOpen')) return;
|
|
265
|
+
if (panel) {
|
|
266
|
+
panel.classList.add("hidden");
|
|
267
|
+
panel.classList.remove("project-logs-previewing");
|
|
268
|
+
}
|
|
269
|
+
// Fullscreen is always dropped on close so the next open is a bounded pane
|
|
270
|
+
// and never silently hides the conversation.
|
|
271
|
+
applyWindowState(store.get('projectLogsWide'), false);
|
|
272
|
+
var button = document.getElementById("project-logs-btn");
|
|
273
|
+
if (button) button.classList.remove("active");
|
|
274
|
+
store.set({ projectLogsOpen: false, projectLogsPinned: false });
|
|
275
|
+
syncAmbient();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// --- Server messages -----------------------------------------------------
|
|
279
|
+
|
|
280
|
+
export function handleProjectLogsState(msg) {
|
|
281
|
+
if (msg.requestId && msg.requestId !== store.get('projectLogsListRequestId')) return;
|
|
282
|
+
store.set({ projectLogsEntries: msg.entries || [] });
|
|
283
|
+
if (!listEl) return;
|
|
284
|
+
if (filterEl && Array.isArray(msg.categories)) {
|
|
285
|
+
renderFilter(filterEl, msg.categories, store.get('projectLogsCategory'), applyFilter);
|
|
286
|
+
}
|
|
287
|
+
renderList(listEl, msg.entries || [], requestEntry);
|
|
288
|
+
if (store.get('projectLogsView') !== "detail") listEl.scrollTop = store.get('projectLogsListScroll') || 0;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function handleProjectLogEntry(msg) {
|
|
292
|
+
if (msg.requestId && msg.requestId !== store.get('projectLogsReadRequestId')) return;
|
|
293
|
+
if (!msg.entry) return;
|
|
294
|
+
showDetail(msg.entry);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export function handleProjectLogCommented(msg) {
|
|
298
|
+
if (msg.requestId && msg.requestId !== store.get('projectLogsCommentRequestId')) return;
|
|
299
|
+
if (!msg.entry) return;
|
|
300
|
+
// The comment is a proposal, not a change, so say so plainly rather than
|
|
301
|
+
// leaving the composer reading "Posting..." forever.
|
|
302
|
+
var pendingStatus = store.get('projectLogsCommentStatusEl');
|
|
303
|
+
if (pendingStatus) pendingStatus.textContent = "Awaiting Project Driver review";
|
|
304
|
+
store.set({ projectLogsCommentStatusEl: null });
|
|
305
|
+
// Re-render the open entry so the new comment and its attribution appear,
|
|
306
|
+
// then refresh the ledger so the comment count is current.
|
|
307
|
+
showDetail(msg.entry);
|
|
308
|
+
requestList();
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// A canonical revision landed. This marks the ambient cues and never opens,
|
|
312
|
+
// focuses, or scrolls anything.
|
|
313
|
+
export function handleProjectLogUpdated(msg) {
|
|
314
|
+
noteCanonicalUpdate(msg);
|
|
315
|
+
// Refresh the ledger only when it is already on screen, so the row moves to
|
|
316
|
+
// the top the moment its canonical update arrives.
|
|
317
|
+
if (store.get('projectLogsOpen') && store.get('projectLogsView') !== "detail") requestList();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function handleProjectLogsError(msg) {
|
|
321
|
+
var pending = [
|
|
322
|
+
store.get('projectLogsListRequestId'),
|
|
323
|
+
store.get('projectLogsReadRequestId'),
|
|
324
|
+
store.get('projectLogsCommentRequestId')
|
|
325
|
+
];
|
|
326
|
+
if (msg.requestId && pending.indexOf(msg.requestId) === -1) return;
|
|
327
|
+
showToast(msg.message || "Project Logs could not complete the request.", "error");
|
|
328
|
+
}
|
|
@@ -560,6 +560,15 @@ export function handleInstructionsWrite(msg) {
|
|
|
560
560
|
|
|
561
561
|
// ===== Environment (key-value list) =====
|
|
562
562
|
var envSaveTimer = null;
|
|
563
|
+
var envStatusTimer = null;
|
|
564
|
+
|
|
565
|
+
function showEnvSaveStatus(elementId, text, duration) {
|
|
566
|
+
var saveStatus = document.getElementById(elementId);
|
|
567
|
+
if (!saveStatus) return;
|
|
568
|
+
if (envStatusTimer) clearTimeout(envStatusTimer);
|
|
569
|
+
saveStatus.textContent = text;
|
|
570
|
+
envStatusTimer = setTimeout(function () { saveStatus.textContent = ""; }, duration);
|
|
571
|
+
}
|
|
563
572
|
|
|
564
573
|
function loadEnvironment() {
|
|
565
574
|
var saveStatus = document.getElementById("ps-env-save-status");
|
|
@@ -571,9 +580,6 @@ function loadEnvironment() {
|
|
|
571
580
|
}
|
|
572
581
|
|
|
573
582
|
export function handleProjectEnv(msg) {
|
|
574
|
-
var notice = document.getElementById("ps-env-override-notice");
|
|
575
|
-
if (notice) notice.classList.toggle("hidden", !msg.hasEnvrc);
|
|
576
|
-
|
|
577
583
|
// Parse envrc string into key-value pairs
|
|
578
584
|
var list = document.getElementById("ps-env-list");
|
|
579
585
|
if (!list) return;
|
|
@@ -606,9 +612,10 @@ export function parseEnvString(str) {
|
|
|
606
612
|
if (eq === -1) continue;
|
|
607
613
|
var key = line.substring(0, eq).trim();
|
|
608
614
|
var val = line.substring(eq + 1).trim();
|
|
609
|
-
//
|
|
610
|
-
if (
|
|
611
|
-
|
|
615
|
+
// Saved values are JSON-quoted so shell metacharacters remain data.
|
|
616
|
+
if (val.charAt(0) === '"' && val.charAt(val.length - 1) === '"') {
|
|
617
|
+
try { val = JSON.parse(val); } catch (e) { val = val.substring(1, val.length - 1); }
|
|
618
|
+
} else if (val.charAt(0) === "'" && val.charAt(val.length - 1) === "'") {
|
|
612
619
|
val = val.substring(1, val.length - 1);
|
|
613
620
|
}
|
|
614
621
|
if (key) pairs.push({ key: key, value: val });
|
|
@@ -616,6 +623,10 @@ export function parseEnvString(str) {
|
|
|
616
623
|
return pairs;
|
|
617
624
|
}
|
|
618
625
|
|
|
626
|
+
export function serializeEnvValue(value) {
|
|
627
|
+
return JSON.stringify(value == null ? "" : String(value));
|
|
628
|
+
}
|
|
629
|
+
|
|
619
630
|
function buildEnvString() {
|
|
620
631
|
var list = document.getElementById("ps-env-list");
|
|
621
632
|
if (!list) return "";
|
|
@@ -626,7 +637,7 @@ function buildEnvString() {
|
|
|
626
637
|
var valInput = rows[i].querySelector(".ps-env-val");
|
|
627
638
|
var key = keyInput ? keyInput.value.trim() : "";
|
|
628
639
|
var val = valInput ? valInput.value : "";
|
|
629
|
-
if (key) lines.push("export " + key + "=" + val);
|
|
640
|
+
if (key) lines.push("export " + key + "=" + serializeEnvValue(val));
|
|
630
641
|
}
|
|
631
642
|
return lines.join("\n");
|
|
632
643
|
}
|
|
@@ -719,28 +730,30 @@ function autoSaveEnv() {
|
|
|
719
730
|
var envrc = buildEnvString();
|
|
720
731
|
if (ctx.ws && ctx.connected) {
|
|
721
732
|
ctx.ws.send(JSON.stringify({ type: "set_project_env", slug: currentSlug, envrc: envrc }));
|
|
722
|
-
|
|
723
|
-
if (saveStatus) {
|
|
724
|
-
saveStatus.textContent = "Saved";
|
|
725
|
-
setTimeout(function () { saveStatus.textContent = ""; }, 2000);
|
|
726
|
-
}
|
|
733
|
+
showEnvSaveStatus("ps-env-save-status", "Saving...", 2000);
|
|
727
734
|
}
|
|
728
735
|
}, 800);
|
|
729
736
|
}
|
|
730
737
|
|
|
731
738
|
export function handleProjectEnvSaved(msg) {
|
|
732
|
-
var saveStatus = document.getElementById("ps-env-save-status");
|
|
733
|
-
if (!saveStatus) return;
|
|
734
739
|
if (msg.ok) {
|
|
735
|
-
|
|
736
|
-
setTimeout(function () { saveStatus.textContent = ""; }, 2000);
|
|
740
|
+
showEnvSaveStatus("ps-env-save-status", msg.timing || "Saved", 5000);
|
|
737
741
|
} else {
|
|
738
|
-
|
|
742
|
+
showEnvSaveStatus("ps-env-save-status", "Error: " + (msg.error || "Failed to save"), 5000);
|
|
739
743
|
}
|
|
740
744
|
}
|
|
741
745
|
|
|
742
746
|
// ===== Shared Environment (via tabs) =====
|
|
743
747
|
var sharedEnvSaveTimer = null;
|
|
748
|
+
var sharedEnvStatusTimer = null;
|
|
749
|
+
|
|
750
|
+
function showSharedEnvSaveStatus(text, duration) {
|
|
751
|
+
var saveStatus = document.getElementById("ps-shared-env-save-status");
|
|
752
|
+
if (!saveStatus) return;
|
|
753
|
+
if (sharedEnvStatusTimer) clearTimeout(sharedEnvStatusTimer);
|
|
754
|
+
saveStatus.textContent = text;
|
|
755
|
+
sharedEnvStatusTimer = setTimeout(function () { saveStatus.textContent = ""; }, duration);
|
|
756
|
+
}
|
|
744
757
|
|
|
745
758
|
function loadSharedEnv() {
|
|
746
759
|
var saveStatus = document.getElementById("ps-shared-env-save-status");
|
|
@@ -764,13 +777,10 @@ export function handleProjectSharedEnv(msg) {
|
|
|
764
777
|
}
|
|
765
778
|
|
|
766
779
|
export function handleProjectSharedEnvSaved(msg) {
|
|
767
|
-
var saveStatus = document.getElementById("ps-shared-env-save-status");
|
|
768
|
-
if (!saveStatus) return;
|
|
769
780
|
if (msg.ok) {
|
|
770
|
-
|
|
771
|
-
setTimeout(function () { saveStatus.textContent = ""; }, 2000);
|
|
781
|
+
showSharedEnvSaveStatus(msg.timing || "Saved", 5000);
|
|
772
782
|
} else {
|
|
773
|
-
|
|
783
|
+
showSharedEnvSaveStatus("Error: " + (msg.error || "Failed to save"), 5000);
|
|
774
784
|
}
|
|
775
785
|
}
|
|
776
786
|
|
|
@@ -784,7 +794,7 @@ function buildSharedEnvString() {
|
|
|
784
794
|
var valInput = rows[i].querySelector(".ps-env-val");
|
|
785
795
|
var key = keyInput ? keyInput.value.trim() : "";
|
|
786
796
|
var val = valInput ? valInput.value : "";
|
|
787
|
-
if (key) lines.push("export " + key + "=" + val);
|
|
797
|
+
if (key) lines.push("export " + key + "=" + serializeEnvValue(val));
|
|
788
798
|
}
|
|
789
799
|
return lines.join("\n");
|
|
790
800
|
}
|
|
@@ -873,11 +883,7 @@ function autoSaveSharedEnv() {
|
|
|
873
883
|
var envrc = buildSharedEnvString();
|
|
874
884
|
if (ctx.ws && ctx.connected) {
|
|
875
885
|
ctx.ws.send(JSON.stringify({ type: "set_shared_env", envrc: envrc }));
|
|
876
|
-
|
|
877
|
-
if (saveStatus) {
|
|
878
|
-
saveStatus.textContent = "Saved";
|
|
879
|
-
setTimeout(function () { saveStatus.textContent = ""; }, 2000);
|
|
880
|
-
}
|
|
886
|
+
showSharedEnvSaveStatus("Saving...", 2000);
|
|
881
887
|
}
|
|
882
888
|
}, 800);
|
|
883
889
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Two modes share the same overlay:
|
|
4
4
|
// Cmd/Ctrl+E → projects only (non-Mate), activates via switchProject
|
|
5
|
-
// Cmd/Ctrl+M → Mates only, activates
|
|
5
|
+
// Cmd/Ctrl+M → Mates only, activates in the home conversation pane
|
|
6
6
|
//
|
|
7
7
|
// While the modifier is still held, repeating the same shortcut cycles
|
|
8
8
|
// forward and Shift cycles back. Releasing the modifier commits —
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
import { store } from './store.js';
|
|
17
17
|
import { refreshIcons } from './icons.js';
|
|
18
18
|
import { getCachedProjects, switchProject } from './app-projects.js';
|
|
19
|
-
import {
|
|
19
|
+
import { showHomeHub } from './app-home-hub.js';
|
|
20
|
+
import { openHomeChat } from './home-mate-chat.js';
|
|
20
21
|
import { mateAvatarUrl } from './avatar.js';
|
|
21
22
|
import { parseEmojis } from './markdown.js';
|
|
22
23
|
import { isExternalWorktree, externalWorktreeTooltip, appendExternalWorktreeBadge } from './worktree-location.js';
|
|
@@ -33,6 +34,7 @@ var _entries = [];
|
|
|
33
34
|
|
|
34
35
|
export function openSwitcherForMode(mode) {
|
|
35
36
|
if (mode !== 'project' && mode !== 'mate') return;
|
|
37
|
+
if (mode === 'mate' && store.get('projectMateDmsEnabled') === false) return;
|
|
36
38
|
if (_open) closeSwitcher(false);
|
|
37
39
|
openSwitcher(mode);
|
|
38
40
|
}
|
|
@@ -93,11 +95,11 @@ export function initProjectSwitcher() {
|
|
|
93
95
|
function refreshMateHintVisibility() {
|
|
94
96
|
if (!mateHint) return;
|
|
95
97
|
var mates = store.get('cachedMatesList') || [];
|
|
96
|
-
mateHint.classList.toggle('hidden', mates.length === 0);
|
|
98
|
+
mateHint.classList.toggle('hidden', mates.length === 0 || store.get('projectMateDmsEnabled') === false);
|
|
97
99
|
}
|
|
98
100
|
refreshMateHintVisibility();
|
|
99
101
|
store.subscribe(function (state, prev) {
|
|
100
|
-
if (state.cachedMatesList !== prev.cachedMatesList) refreshMateHintVisibility();
|
|
102
|
+
if (state.cachedMatesList !== prev.cachedMatesList || state.projectMateDmsEnabled !== prev.projectMateDmsEnabled) refreshMateHintVisibility();
|
|
101
103
|
});
|
|
102
104
|
}
|
|
103
105
|
|
|
@@ -143,6 +145,7 @@ function handleKeyDown(e) {
|
|
|
143
145
|
// same shortcut cycles.
|
|
144
146
|
if (isMod && (k === 'e' || k === 'm')) {
|
|
145
147
|
var requestedMode = k === 'e' ? 'project' : 'mate';
|
|
148
|
+
if (requestedMode === 'mate' && store.get('projectMateDmsEnabled') === false) return;
|
|
146
149
|
e.preventDefault();
|
|
147
150
|
e.stopPropagation();
|
|
148
151
|
if (!_open) {
|
|
@@ -298,7 +301,10 @@ function closeSwitcher(commit) {
|
|
|
298
301
|
var entry = _entries[_highlightedIndex];
|
|
299
302
|
if (!entry || entry.disabled) return;
|
|
300
303
|
if (_mode === 'mate') {
|
|
301
|
-
if (entry.key !== currentActiveKey())
|
|
304
|
+
if (entry.key !== currentActiveKey()) {
|
|
305
|
+
showHomeHub();
|
|
306
|
+
openHomeChat(entry.key);
|
|
307
|
+
}
|
|
302
308
|
} else {
|
|
303
309
|
if (entry.key !== store.get('currentSlug')) switchProject(entry.key);
|
|
304
310
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { addToMessages, scrollToBottom } from './app-rendering.js';
|
|
2
|
+
import { createWorkspaceAssignmentCard, showWorkspaceAssignmentError } from './workspace-assignment-card.js';
|
|
3
|
+
|
|
4
|
+
function findCard(assignmentId) {
|
|
5
|
+
var cards = document.querySelectorAll(".workspace-assignment-card:not(.home-workspace-assignment)");
|
|
6
|
+
for (var i = 0; i < cards.length; i++) if (cards[i].dataset.assignmentId === assignmentId) return cards[i];
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function renderProjectWorkspaceAssignment(msg) {
|
|
11
|
+
var assignment = msg && msg.assignment;
|
|
12
|
+
if (!assignment || !assignment.assignmentId) return false;
|
|
13
|
+
var previous = findCard(assignment.assignmentId);
|
|
14
|
+
var card = createWorkspaceAssignmentCard(assignment, { home: false });
|
|
15
|
+
if (previous && previous.parentNode) previous.parentNode.replaceChild(card, previous);
|
|
16
|
+
else addToMessages(card);
|
|
17
|
+
scrollToBottom();
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function showProjectWorkspaceAssignmentError(msg) {
|
|
22
|
+
return showWorkspaceAssignmentError(findCard(msg && msg.assignmentId), msg || {});
|
|
23
|
+
}
|
|
@@ -125,8 +125,8 @@ export function initScheduler(_ctx) {
|
|
|
125
125
|
function ensurePanel() {
|
|
126
126
|
if (panel) return;
|
|
127
127
|
|
|
128
|
-
var
|
|
129
|
-
if (!
|
|
128
|
+
var hostEl = document.getElementById("main-area");
|
|
129
|
+
if (!hostEl) return;
|
|
130
130
|
|
|
131
131
|
panel = document.createElement("div");
|
|
132
132
|
panel.id = "scheduler-panel";
|
|
@@ -256,7 +256,7 @@ function ensurePanel() {
|
|
|
256
256
|
bodyRow.appendChild(content);
|
|
257
257
|
panel.appendChild(bodyRow);
|
|
258
258
|
|
|
259
|
-
|
|
259
|
+
hostEl.appendChild(panel);
|
|
260
260
|
|
|
261
261
|
// --- Close button (in top bar) ---
|
|
262
262
|
panel.querySelector("#scheduler-panel-close").addEventListener("click", function () {
|
|
@@ -383,6 +383,8 @@ function openScheduler() {
|
|
|
383
383
|
|
|
384
384
|
var sidebarBtn = document.getElementById("scheduler-btn");
|
|
385
385
|
if (sidebarBtn) sidebarBtn.classList.add("active");
|
|
386
|
+
var homeBtn = document.getElementById("home-scheduler-btn");
|
|
387
|
+
if (homeBtn) homeBtn.classList.add("active");
|
|
386
388
|
|
|
387
389
|
// Persist scheduler state in URL hash
|
|
388
390
|
if (location.hash !== "#scheduler") {
|
|
@@ -423,6 +425,8 @@ export function closeScheduler() {
|
|
|
423
425
|
// Un-mark sidebar button
|
|
424
426
|
var sidebarBtn = document.getElementById("scheduler-btn");
|
|
425
427
|
if (sidebarBtn) sidebarBtn.classList.remove("active");
|
|
428
|
+
var homeBtn = document.getElementById("home-scheduler-btn");
|
|
429
|
+
if (homeBtn) homeBtn.classList.remove("active");
|
|
426
430
|
|
|
427
431
|
// Remove scheduler hash from URL
|
|
428
432
|
if (location.hash === "#scheduler") {
|
|
@@ -1470,6 +1474,16 @@ export function openSchedulerToTab(tab) {
|
|
|
1470
1474
|
}
|
|
1471
1475
|
}
|
|
1472
1476
|
|
|
1477
|
+
export function openHomeScheduler() {
|
|
1478
|
+
if (!ctx) return;
|
|
1479
|
+
ctx.requireClayRalph(function () {
|
|
1480
|
+
showAllProjects = true;
|
|
1481
|
+
openSchedulerToTab("calendar");
|
|
1482
|
+
renderSidebar();
|
|
1483
|
+
render();
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1473
1487
|
export function isSchedulerOpen() {
|
|
1474
1488
|
return panelOpen;
|
|
1475
1489
|
}
|