mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-05 06:59:42 +02:00
style: drop em-dashes from the prose added in this branch
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -44,7 +44,7 @@ export interface SessionSearchInput {
|
||||
/** Recency timestamp (e.g. lastActivityAt or createdAt). */
|
||||
timestamp: number;
|
||||
/**
|
||||
* True for a session that is no longer running (issue #261 — past sessions come
|
||||
* True for a session that is no longer running (issue #261, past sessions come
|
||||
* from the history index, not the live map). Such a result resumes the
|
||||
* conversation instead of switching to a tab that no longer exists.
|
||||
*/
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
/**
|
||||
* Narrowest window that gets the column. The welcome content is 560px wide and
|
||||
* centered, so at 1180px each gutter is 310px — enough for the 256px column plus
|
||||
* centered, so at 1180px each gutter is 310px, enough for the 256px column plus
|
||||
* its 20px offset and still a visible gap. Anything narrower would overlap the
|
||||
* search panel, which is why this is a width gate and not a device-type gate.
|
||||
*/
|
||||
|
||||
@@ -3737,7 +3737,7 @@ body.touch-device .terminal-container .xterm .xterm-helper-textarea {
|
||||
}
|
||||
|
||||
/* Title row for the past-session list: label + count on the left, filter and
|
||||
sort on the right (issue #260 — 35 conversations in a 4-row box). */
|
||||
sort on the right (issue #260, 35 conversations in a 4-row box). */
|
||||
.history-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -1958,7 +1958,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
|
||||
/**
|
||||
* How many past sessions the home screen loads (also the filter/sort corpus).
|
||||
* 200, not the old 60, so the filter can reach a real backlog — an install with
|
||||
* 200, not the old 60, so the filter can reach a real backlog, an install with
|
||||
* 35+ conversations would otherwise hit the ceiling before the filter is useful
|
||||
* (raised in @jordan8037310's #263; the endpoint clamps at 500).
|
||||
*/
|
||||
@@ -1989,7 +1989,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
|
||||
// Keep the corpus around: filtering and sorting (issue #260) work on this
|
||||
// array, so a re-render costs no request. Expansion survives the periodic
|
||||
// refresh in panels-ui.js — collapsing the list under the user's cursor
|
||||
// refresh in panels-ui.js, collapsing the list under the user's cursor
|
||||
// every few seconds would be worse than the original 4-item cap.
|
||||
this._historyAll = allSessions;
|
||||
this._historyCases = cases;
|
||||
@@ -2005,7 +2005,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
|
||||
/**
|
||||
* Wire the filter box and sort select once; both re-render from the cached
|
||||
* corpus. The sort choice is restored from (and saved to) localStorage — it is
|
||||
* corpus. The sort choice is restored from (and saved to) localStorage, it is
|
||||
* a per-device display preference, so it stays out of the synced settings
|
||||
* schema, same as `codeman:skin`.
|
||||
*/
|
||||
@@ -2021,7 +2021,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
const saved = localStorage.getItem(this._HISTORY_SORT_KEY);
|
||||
if (saved && Array.from(sort.options).some((o) => o.value === saved)) sort.value = saved;
|
||||
} catch {
|
||||
/* private mode — the order just won't persist */
|
||||
/* private mode, the order just won't persist */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2041,7 +2041,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
try {
|
||||
localStorage.setItem(this._HISTORY_SORT_KEY, sort.value);
|
||||
} catch {
|
||||
/* private mode — the order just won't persist */
|
||||
/* private mode, the order just won't persist */
|
||||
}
|
||||
this._renderHistoryList();
|
||||
});
|
||||
@@ -2064,7 +2064,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
/**
|
||||
* The text a history row shows as its title. Most transcript-backed rows have
|
||||
* no session name at all, so this falls through to the first prompt and then
|
||||
* to the path — and the A–Z sort keys off the SAME string, or "sort by name"
|
||||
* to the path, and the A–Z sort keys off the SAME string, or "sort by name"
|
||||
* would silently do nothing for exactly the rows the list is mostly made of.
|
||||
*/
|
||||
_historyRowLabel(s, fallback) {
|
||||
@@ -2074,7 +2074,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
/**
|
||||
* Sort past-session rows. 'recent' keeps the backend order (newest first);
|
||||
* the alphabetical modes sort by the visible title or by folder basename.
|
||||
* Pinned rows stay on top in every mode — pinning is an explicit override and
|
||||
* Pinned rows stay on top in every mode, pinning is an explicit override and
|
||||
* a sort that buried it would read as the pin having been lost.
|
||||
*/
|
||||
_sortHistoryRows(rows, mode) {
|
||||
@@ -2097,7 +2097,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
* Render the "Resume Conversation" list from the cached corpus, applying the
|
||||
* current filter and sort. Collapsed by default to _HISTORY_INITIAL_COUNT;
|
||||
* "Show more" expands the list AND the box (the CSS cap is class-driven, since
|
||||
* a fixed 240px box made expansion pointless — issue #260).
|
||||
* a fixed 240px box made expansion pointless, issue #260).
|
||||
*/
|
||||
_renderHistoryList() {
|
||||
const list = document.getElementById('historyList');
|
||||
@@ -3985,7 +3985,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
/** Render the grouped result cards (or empty/loading states). */
|
||||
_renderSearch(data) {
|
||||
const results = document.getElementById('searchResults');
|
||||
// The header carries the title plus the filter/sort controls (issue #260) —
|
||||
// The header carries the title plus the filter/sort controls (issue #260),
|
||||
// hide the whole row, not just the title, or the controls float above the
|
||||
// search results and act on a list that is not on screen.
|
||||
const historyHeader = document.getElementById('historyHeader') || document.getElementById('historyTitle');
|
||||
@@ -4100,7 +4100,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
/**
|
||||
* Navigate to a search result by jumpTo.kind, reusing the existing app methods:
|
||||
* session → selectSession(sessionId) (open/switch to the session)
|
||||
* resume-session→ resumeHistorySession(...) (past session — no tab to switch to)
|
||||
* resume-session→ resumeHistorySession(...) (past session, no tab to switch to)
|
||||
* run-summary → openRunSummary(sessionId) (session options → summary tab)
|
||||
* file-preview → openFilePreview(path, sessionId, attachmentId)
|
||||
*/
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Registers `GET /api/search?q=&types=&limit=` — a bounded, in-memory search
|
||||
* across three v1 sources, returned in the standard ApiResponse envelope:
|
||||
* 1. sessions/cases — name, working directory, session id, for LIVE sessions
|
||||
* 1. sessions/cases, name, working directory, session id, for LIVE sessions
|
||||
* plus the past-session snapshot in `session-history-index.ts` (issue #261:
|
||||
* the live map alone made every closed session unfindable by folder name)
|
||||
* 2. run-summary events — event title/details (from the live run-summary trackers)
|
||||
@@ -106,7 +106,7 @@ function harvestSources(ctx: SessionPort & InfraPort, canSee?: (owner?: string)
|
||||
}
|
||||
|
||||
// Past sessions: the out-of-band snapshot of the unified list. Unscoped on
|
||||
// disk, so every row goes through the same ownership check as a live one —
|
||||
// disk, so every row goes through the same ownership check as a live one,
|
||||
// host-wide transcript rows carry no owner and are therefore admin-only in
|
||||
// multi-user mode, matching GET /api/sessions/unified.
|
||||
for (const item of getHistorySessionIndex().items) {
|
||||
|
||||
@@ -3549,7 +3549,7 @@ export function registerSessionRoutes(
|
||||
* Gather the four read-only views the unified list is merged from, plus mux
|
||||
* stats. This is the expensive half (the lifecycle log and a scan of every
|
||||
* Claude transcript), factored out of the route handler because the
|
||||
* past-session search index rebuilds itself from the very same inputs — off
|
||||
* past-session search index rebuilds itself from the very same inputs, off
|
||||
* the request path, see session-history-index.ts.
|
||||
*/
|
||||
async function gatherUnifiedInputs(): Promise<{
|
||||
@@ -3665,7 +3665,7 @@ export function registerSessionRoutes(
|
||||
/**
|
||||
* Publish a merged unified list as the past-session search index (issue #261).
|
||||
* The snapshot is stored UNSCOPED with a per-row owner, so it must only ever be
|
||||
* built from an unscoped merge — `harvestSources()` in search-routes re-applies
|
||||
* built from an unscoped merge, `harvestSources()` in search-routes re-applies
|
||||
* the ownership check on read.
|
||||
*/
|
||||
function publishHistorySessionIndex(merged: UnifiedSessionItem[]): void {
|
||||
@@ -3731,7 +3731,7 @@ export function registerSessionRoutes(
|
||||
mux,
|
||||
});
|
||||
|
||||
// Refresh the search index off the back of this request — the home screen
|
||||
// Refresh the search index off the back of this request, the home screen
|
||||
// fetches this endpoint whenever it opens, which is the same screen the
|
||||
// search box lives on, so the snapshot is warm before anyone types. A scoped
|
||||
// merge is a per-user subset and would corrupt the shared snapshot, so that
|
||||
|
||||
@@ -5,31 +5,31 @@
|
||||
* session map alone, so a folder sitting in the home screen's "Resume
|
||||
* Conversation" list matched nothing (issue #261). The corpus that list renders
|
||||
* comes from `GET /api/sessions/unified`, which reads the lifecycle log and every
|
||||
* Claude transcript file — disk I/O the search path deliberately does not do
|
||||
* (its no-fs property is what keeps a per-keystroke query cheap and traversal-free).
|
||||
* Claude transcript file: disk I/O the search path deliberately does not do (its
|
||||
* no-fs property is what keeps a per-keystroke query cheap and traversal-free).
|
||||
*
|
||||
* This module is the seam between the two: a capped snapshot of the unified list
|
||||
* that the search route reads synchronously, refreshed OUT of the request path.
|
||||
* Two things fill it:
|
||||
* 1. `/api/sessions/unified` writes it as a side effect (free — it just merged
|
||||
* 1. `/api/sessions/unified` writes it as a side effect (free, it just merged
|
||||
* that list). The home screen calls that endpoint whenever it opens, which
|
||||
* is the same screen the search box lives on, so it is warm in practice.
|
||||
* 2. `ensureHistorySessionIndexFresh()` — fire-and-forget, single-flight,
|
||||
* TTL-guarded — kicks the registered refresher when a search finds the
|
||||
* 2. `ensureHistorySessionIndexFresh()`, fire-and-forget, single-flight,
|
||||
* TTL-guarded, kicks the registered refresher when a search finds the
|
||||
* snapshot stale. The caller never awaits it: the current query answers from
|
||||
* the existing snapshot and the next one sees fresh data.
|
||||
*
|
||||
* OWNERSHIP: each item carries the `owner` of the session it came from, and rows
|
||||
* not tied to any live/persisted session (host-wide transcript history) carry
|
||||
* `owner: undefined`. `canAccessOwned()` then reproduces the unified route's rule
|
||||
* exactly — in multi-user mode a non-admin sees neither other users' sessions nor
|
||||
* exactly, in multi-user mode a non-admin sees neither other users' sessions nor
|
||||
* unowned host-wide history, and in single-user mode every check short-circuits
|
||||
* true. The snapshot is written UNSCOPED, so it must never be returned unfiltered.
|
||||
*
|
||||
* Key exports:
|
||||
* - setHistorySessionIndex / getHistorySessionIndex — the snapshot accessors.
|
||||
* - buildHistorySessionIndexItems — pure merged-list → index-item projection.
|
||||
* - setHistoryIndexRefresher / ensureHistorySessionIndexFresh — the refresh hook.
|
||||
* - setHistorySessionIndex / getHistorySessionIndex: the snapshot accessors.
|
||||
* - buildHistorySessionIndexItems: pure merged-list → index-item projection.
|
||||
* - setHistoryIndexRefresher / ensureHistorySessionIndexFresh: the refresh hook.
|
||||
*/
|
||||
|
||||
/** One past-session row in the snapshot. Mirrors what the search corpus needs, nothing more. */
|
||||
@@ -38,7 +38,7 @@ export interface HistorySessionIndexItem {
|
||||
sessionId: string;
|
||||
/** Display name, may be empty for a transcript-only row. */
|
||||
name: string;
|
||||
/** Absolute working directory — the field issue #261 is about matching. */
|
||||
/** Absolute working directory, the field issue #261 is about matching. */
|
||||
workingDir: string;
|
||||
/** Claude conversation UUID, when known: what a resume actually replays. */
|
||||
claudeSessionId?: string;
|
||||
@@ -47,7 +47,7 @@ export interface HistorySessionIndexItem {
|
||||
/**
|
||||
* Owning user, when the row is tied to a live or persisted session. `undefined`
|
||||
* means host-wide transcript history, which only admins (or single-user mode)
|
||||
* may see — the same rule `/api/sessions/unified` applies.
|
||||
* may see, the same rule `/api/sessions/unified` applies.
|
||||
*/
|
||||
owner?: string;
|
||||
/** True when the session is still in the live map (search harvests those directly). */
|
||||
@@ -81,7 +81,7 @@ export interface MergedSessionLike {
|
||||
}
|
||||
|
||||
/**
|
||||
* Project a merged unified list into index items. PURE — the caller supplies the
|
||||
* Project a merged unified list into index items. PURE, the caller supplies the
|
||||
* owner lookup and the live-id set it already has in hand.
|
||||
*
|
||||
* Rows with no working directory AND no name are dropped: they can never match a
|
||||
@@ -121,7 +121,7 @@ export function setHistorySessionIndex(items: HistorySessionIndexItem[], now = D
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the snapshot. The returned array is UNSCOPED — callers must apply the
|
||||
* Read the snapshot. The returned array is UNSCOPED, callers must apply the
|
||||
* per-item ownership check before exposing any of it.
|
||||
*/
|
||||
export function getHistorySessionIndex(): HistorySessionIndexSnapshot {
|
||||
@@ -142,7 +142,7 @@ export function setHistoryIndexRefresher(fn: (() => Promise<void>) | null): void
|
||||
}
|
||||
|
||||
/**
|
||||
* Kick a background rebuild if the snapshot is stale. Returns immediately —
|
||||
* Kick a background rebuild if the snapshot is stale. Returns immediately,
|
||||
* NEVER await this from a request handler, that is the whole point: the search
|
||||
* path answers from the current snapshot and stays free of disk I/O.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user