mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-04 14:39:42 +02:00
Merge pull request #507 from irisitymichaelgrundberg/feat/select-session-from-url
feat(web): select a dashboard session from a #session=<id> link
This commit is contained in:
@@ -602,6 +602,9 @@ class CodemanApp {
|
||||
// service-worker shell loads), with the server-injected global as a fallback.
|
||||
this.soloSessionId = this._detectSoloSessionId();
|
||||
this.isSoloWindow = !!this.soloSessionId;
|
||||
// A session another page asked for with a `#session=<id>` link. It waits
|
||||
// here until the session list has that id (see _selectUrlSession).
|
||||
this._urlSessionId = this.isSoloWindow ? null : this._takeUrlSession();
|
||||
this.detachedSessions = new Set(); // dashboard-side: ids currently popped out
|
||||
this.detachedWindows = new Map(); // dashboard-side: id -> WindowProxy
|
||||
this._detachWatchTimers = new Map(); // dashboard-side: id -> setInterval handle
|
||||
@@ -997,6 +1000,16 @@ class CodemanApp {
|
||||
// strip never flashes before handleInit selects the target session.
|
||||
this._initWindowChannel();
|
||||
if (this.isSoloWindow) document.body.classList.add('solo-mode');
|
||||
// A page holding this window switches its tab by changing only the
|
||||
// fragment, which keeps the page loaded (see sessionIdFromFragment).
|
||||
if (!this.isSoloWindow) {
|
||||
window.addEventListener('hashchange', () => {
|
||||
const id = this._takeUrlSession();
|
||||
if (!id) return;
|
||||
this._urlSessionId = id;
|
||||
this._selectUrlSession();
|
||||
});
|
||||
}
|
||||
// Initialize mobile handlers
|
||||
KeyboardHandler.init();
|
||||
SwipeHandler.init();
|
||||
@@ -1368,6 +1381,33 @@ class CodemanApp {
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
/** Read a `#session=<id>` link off the URL and drop the fragment. The next
|
||||
* link to the same session is then a change the browser reports, even
|
||||
* after you have clicked away to another tab. Returns the id or null. */
|
||||
_takeUrlSession() {
|
||||
const id = window.CodemanUrlSession?.sessionIdFromFragment(location.hash) ?? null;
|
||||
if (id) {
|
||||
try { history.replaceState(history.state, '', location.pathname + location.search); } catch {}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
/** Show the session a `#session=<id>` link asked for, once the session list
|
||||
* has it. A page that has just created a session can link to it before
|
||||
* session:created arrives here, so an unknown id stays pending and
|
||||
* _onSessionCreated tries again.
|
||||
*
|
||||
* ⚠️ The selection is `auto`. The page that set the fragment may be a
|
||||
* script, and this window may not even be in front, so following a link is
|
||||
* not a human looking at the session and must not spend its idle alert. */
|
||||
_selectUrlSession() {
|
||||
const id = this._urlSessionId;
|
||||
if (!id || !this.sessions.has(id)) return false;
|
||||
this._urlSessionId = null;
|
||||
this.selectSession(id, { auto: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop a session out into its own browser window. SINGLE, idempotent entry
|
||||
* point: the tab's pop-out icon calls this, and a future gesture layer
|
||||
@@ -1949,6 +1989,7 @@ class CodemanApp {
|
||||
this.updateCost();
|
||||
// Start stats polling when first session appears
|
||||
if (this.sessions.size === 1) this.startSystemStatsPolling();
|
||||
if (this._urlSessionId === data.id) this._selectUrlSession();
|
||||
}
|
||||
|
||||
_onSessionUpdated(data) {
|
||||
@@ -4367,6 +4408,13 @@ class CodemanApp {
|
||||
return;
|
||||
}
|
||||
|
||||
// A `#session=<id>` link wins over restoring the last active tab.
|
||||
if (this._urlSessionId && this.sessions.has(this._urlSessionId)) {
|
||||
this.activeSessionId = null;
|
||||
this._selectUrlSession();
|
||||
return;
|
||||
}
|
||||
|
||||
const previousActiveId = this.activeSessionId;
|
||||
if (this.sessionOrder.length === 0) {
|
||||
this.activeSessionId = null;
|
||||
@@ -6560,6 +6608,12 @@ class CodemanApp {
|
||||
}
|
||||
|
||||
async selectSession(sessionId, options = {}) {
|
||||
// Picking another tab yourself retires a `#session=<id>` link still
|
||||
// waiting for its session, which would otherwise take the tab from you
|
||||
// whenever that session turned up (see _selectUrlSession).
|
||||
if (options?.auto !== true && this._urlSessionId && this._urlSessionId !== sessionId) {
|
||||
this._urlSessionId = null;
|
||||
}
|
||||
// If this session is popped out into its own window, raise that window
|
||||
// instead of showing it inline (focus-on-click for detached tabs). If we
|
||||
// owned a now-closed window, _raiseDetached re-docks and returns false so
|
||||
|
||||
@@ -1853,10 +1853,27 @@ function reconcilePtyGeometry(local, pty) {
|
||||
return { adopt: true, cols: pty.cols };
|
||||
}
|
||||
|
||||
/**
|
||||
* Which session does a dashboard URL's fragment ask for? Another page that
|
||||
* holds the dashboard's window, such as a task board, points it at
|
||||
* `/#session=<id>`. Only the fragment changes between two such links, so the
|
||||
* browser keeps the page loaded and fires `hashchange`, and the dashboard
|
||||
* switches tabs without reloading. Any other fragment asks for nothing.
|
||||
*
|
||||
* @param {string} hash - `location.hash`, with or without its leading `#`
|
||||
* @returns {string|null} the session id, or null
|
||||
*/
|
||||
function sessionIdFromFragment(hash) {
|
||||
const params = new URLSearchParams(String(hash || '').replace(/^#/, ''));
|
||||
const id = params.get('session');
|
||||
return id && id.trim() ? id.trim() : null;
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.CodemanHistoryFormat = { formatHistoryBytes, computeHistoryTruncationNotice, computeRewriteScrollLine };
|
||||
window.CodemanFilePaths = { absoluteFilePathPattern, previewsInFileViewer, FILE_PREVIEW_EXTENSIONS };
|
||||
window.CodemanTerminalLines = { terminalLogicalLine };
|
||||
window.CodemanUrlSession = { sessionIdFromFragment };
|
||||
window.CodemanSplitPane = {
|
||||
clampDividerPercent,
|
||||
buildSplitPickerSessions,
|
||||
|
||||
Reference in New Issue
Block a user